Anonymous avatar Anonymous committed 9dc5b2a Merge

Merge viewer-pre-beta -> seraph-viewer-pre-beta

Comments (0)

Files changed (187)

 345b17e7cf630db77e840b4fe3451bd476d750a3 76f586a8e22b
 0000000000000000000000000000000000000000 76f586a8e22b
 54d772d8687c69b1d773f6ce14bbc7bdc9d6c05f 2.5.0-beta2
+b542f8134a2bb5dd054ff4e509a44b2ee463b1bf nat-eventapi2-base
 7076e22f9f43f479a4ea75eac447a36364bead5a DRTVWR-5_2.2.0-beta1
 9822eb3e25f7fe0c28ffd8aba45c507caa383cbc DRTVWR-3_2.2.0-beta2
 b0cd7e150009809a0b5b0a9d5785cd4bb230413a DRTVWR-7_2.2.0-beta3
 b723921b5c711bd24dbe77dc76ef488b544dac78 2.5.0-release
 b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-31_2.5.0-release
 92e58e51776a4f8c29069b1a62ff21454d2085f0 2.6.0-start
+3178e311da3a8739a85363665006ea3c4610cad4 dons-headless-hackathon-work
 63a6aedfce785a6c760377bf685b2dae616797d2 2.5.1-start
 4dede9ae1ec74d41f6887719f6f1de7340d8578d 2.5.1-release
 4dede9ae1ec74d41f6887719f6f1de7340d8578d DRTVWR-37_2.5.1-release
 c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-start
 c5bdef3aaa2744626aef3c217ce29e1900d357b3 DRTVWR-43_2.6.1-beta1
 c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-beta1
-9e4641f4a7870c0f565a25a2971368d5a29516a1 DRTVWR-41_2.6.0-beta2
-9e4641f4a7870c0f565a25a2971368d5a29516a1 2.6.0-beta2
+c9182ed77d427c759cfacf49a7b71a2e20d522aa DRTVWR-42_2.6.1-release
+c9182ed77d427c759cfacf49a7b71a2e20d522aa 2.6.1-release
 56b2778c743c2a964d82e1caf11084d76a87de2c 2.6.2-start
 42f32494bac475d0737799346f6831558ae8bf5d DRTVWR-39_2.6.0-release
 42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release
 d1203046bb653b763f835b04d184646949d8dd5c DRTVWR-45_2.6.2-beta1
 d1203046bb653b763f835b04d184646949d8dd5c 2.6.2-beta1
+214180ad5714ce8392b82bbebcc92f4babd98300 DRTVWR-44_2.6.2-release
+214180ad5714ce8392b82bbebcc92f4babd98300 2.6.2-release
 52b2263ab28f0976c689fd0b76c55a9eb027cdbf end-of-develop.py
 ec32f1045e7c2644015245df3a9933620aa194b8 2.6.3-start
+d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc DRTVWR-47_2.6.3-beta1
+d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc 2.6.3-beta1
+0630e977504af5ea320c58d33cae4e1ddee793e9 DRTVWR-48_2.6.3-beta2
+0630e977504af5ea320c58d33cae4e1ddee793e9 2.6.3-beta2
+74cd32a06837b0c2cb793b2e8d4d82f5d49462b2 2.6.4-start
+74cd32a06837b0c2cb793b2e8d4d82f5d49462b2 2.6.4-start
+f632f87bb71b0f13d21f2f64b0c42cedb008c749 2.6.4-start
 viewer-pre-release.build_debug_release_separately = true
 viewer-pre-release.build_viewer_update_version_manager = true
 viewer-pre-release.release-viewer.jira = DRTVWR-42
+
+
 # =======================================
 # brad
 # ========================================
-
 debug-halting.email = cg@lindenlab.com
 debug-halting.build_server = false
 debug-halting.build_server_tests = false
 # oz
 # ================
 
-oz-viewer-devreview.build_debug_release_separately = true
-viewer-devreview.build_debug_release_separately = true
-oz_viewer-poreview.build_debug_release_separately = true
+oz_viewer-devreview.build_debug_release_separately = true
 oz-project-1.build_debug_release_separately = true
 oz-project-2.build_debug_release_separately = true
 oz-project-3.build_debug_release_separately = true
 
+
 # ========================================
 # enus
 # ========================================

doc/contributions.txt

 	VWR-592
 Ann Congrejo
 	CT-193
+Ansariel Hiller
+	STORM-1101
 Ardy Lay
 	VWR-19499
 	VWR-24917
 	STORM-1020
 	STORM-1064
 	STORM-960
+	STORM-1101
+	STORM-1108
+	STORM-1094
 	STORM-1077
 	STORM-953
 Kage Pixel
 Kunnis Basiat
 	VWR-82
 	VWR-102
+Lance Corrimal
+	VWR-25269
 Latif Khalifa
 	VWR-5370
 Lisa Lowe
 	VWR-13483
 	VWR-13947
 	VWR-24420
+	STORM-1147
 Thraxis Epsilon
 	SVC-371
 	VWR-383
 	STORM-674
 	STORM-776
 	STORM-825
+	STORM-1098
 	VWR-20741
 	VWR-20933
 Zai Lynch

indra/llcommon/CMakeLists.txt

     llformat.cpp
     llframetimer.cpp
     llheartbeat.cpp
+    llinstancetracker.cpp
     llliveappconfig.cpp
     lllivefile.cpp
     lllog.cpp
   LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
 
   # *TODO - reenable these once tcmalloc libs no longer break the build.
   #ADD_BUILD_TEST(llallocator llcommon)

indra/llcommon/lleventdispatcher.cpp

 #include "llevents.h"
 #include "llerror.h"
 #include "llsdutil.h"
+#include "stringize.h"
+#include <memory>                   // std::auto_ptr
+
+/*****************************************************************************
+*   LLSDArgsSource
+*****************************************************************************/
+/**
+ * Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
+ * if the consumer requests more elements than the array contains.
+ */
+class LL_COMMON_API LLSDArgsSource
+{
+public:
+    LLSDArgsSource(const std::string function, const LLSD& args);
+    ~LLSDArgsSource();
+
+    LLSD next();
+
+    void done() const;
+
+private:
+    std::string _function;
+    LLSD _args;
+    LLSD::Integer _index;
+};
+
+LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
+    _function(function),
+    _args(args),
+    _index(0)
+{
+    if (! (_args.isUndefined() || _args.isArray()))
+    {
+        LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
+                                  << _args << LL_ENDL;
+    }
+}
+
+LLSDArgsSource::~LLSDArgsSource()
+{
+    done();
+}
+
+LLSD LLSDArgsSource::next()
+{
+    if (_index >= _args.size())
+    {
+        LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
+                                  << _args.size() << " provided: " << _args << LL_ENDL;
+    }
+    return _args[_index++];
+}
+
+void LLSDArgsSource::done() const
+{
+    if (_index < _args.size())
+    {
+        LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
+                                   << " of the " << _args.size() << " arguments provided: "
+                                   << _args << LL_ENDL;
+    }
+}
+
+/*****************************************************************************
+*   LLSDArgsMapper
+*****************************************************************************/
+/**
+ * From a formal parameters description and a map of arguments, construct an
+ * arguments array.
+ *
+ * That is, given:
+ * - an LLSD array of length n containing parameter-name strings,
+ *   corresponding to the arguments of a function of interest
+ * - an LLSD collection specifying default parameter values, either:
+ *   - an LLSD array of length m <= n, matching the rightmost m params, or
+ *   - an LLSD map explicitly stating default name=value pairs
+ * - an LLSD map of parameter names and actual values for a particular
+ *   function call
+ * construct an LLSD array of actual argument values for this function call.
+ *
+ * The parameter-names array and the defaults collection describe the function
+ * being called. The map might vary with every call, providing argument values
+ * for the described parameters.
+ *
+ * The array of parameter names must match the number of parameters expected
+ * by the function of interest.
+ *
+ * If you pass a map of default parameter values, it provides default values
+ * as you might expect. It is an error to specify a default value for a name
+ * not listed in the parameters array.
+ *
+ * If you pass an array of default parameter values, it is mapped to the
+ * rightmost m of the n parameter names. It is an error if the default-values
+ * array is longer than the parameter-names array. Consider the following
+ * parameter names: ["a", "b", "c", "d"].
+ *
+ * - An empty array of default values (or an isUndefined() value) asserts that
+ *   every one of the above parameter names is required.
+ * - An array of four default values [1, 2, 3, 4] asserts that every one of
+ *   the above parameters is optional. If the current parameter map is empty,
+ *   they will be passed to the function as [1, 2, 3, 4].
+ * - An array of two default values [11, 12] asserts that parameters "a" and
+ *   "b" are required, while "c" and "d" are optional, having default values
+ *   "c"=11 and "d"=12.
+ *
+ * The arguments array is constructed as follows:
+ *
+ * - Arguments-map keys not found in the parameter-names array are ignored.
+ * - Entries from the map provide values for an improper subset of the
+ *   parameters named in the parameter-names array. This results in a
+ *   tentative values array with "holes." (size of map) + (number of holes) =
+ *   (size of names array)
+ * - Holes are filled with the default values.
+ * - Any remaining holes constitute an error.
+ */
+class LL_COMMON_API LLSDArgsMapper
+{
+public:
+    /// Accept description of function: function name, param names, param
+    /// default values
+    LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
+
+    /// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
+    LLSD map(const LLSD& argsmap) const;
+
+private:
+    static std::string formatlist(const LLSD&);
+
+    // The function-name string is purely descriptive. We want error messages
+    // to be able to indicate which function's LLSDArgsMapper has the problem.
+    std::string _function;
+    // Store the names array pretty much as given.
+    LLSD _names;
+    // Though we're handed an array of name strings, it's more useful to us to
+    // store it as a map from name string to position index. Of course that's
+    // easy to generate from the incoming names array, but why do it more than
+    // once?
+    typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
+    IndexMap _indexes;
+    // Generated array of default values, aligned with the array of param names.
+    LLSD _defaults;
+    // Indicate whether we have a default value for each param.
+    typedef std::vector<char> FilledVector;
+    FilledVector _has_dft;
+};
+
+LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
+                               const LLSD& names, const LLSD& defaults):
+    _function(function),
+    _names(names),
+    _has_dft(names.size())
+{
+    if (! (_names.isUndefined() || _names.isArray()))
+    {
+        LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
+    }
+    LLSD::Integer nparams(_names.size());
+    // From _names generate _indexes.
+    for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
+    {
+        _indexes[_names[ni]] = ni;
+    }
+
+    // Presize _defaults() array so we don't have to resize it more than once.
+    // All entries are initialized to LLSD(); but since _has_dft is still all
+    // 0, they're all "holes" for now.
+    if (nparams)
+    {
+        _defaults[nparams - 1] = LLSD();
+    }
+
+    if (defaults.isUndefined() || defaults.isArray())
+    {
+        LLSD::Integer ndefaults = defaults.size();
+        // defaults is a (possibly empty) array. Right-align it with names.
+        if (ndefaults > nparams)
+        {
+            LL_ERRS("LLSDArgsMapper") << function << " names array " << names
+                                      << " shorter than defaults array " << defaults << LL_ENDL;
+        }
+
+        // Offset by which we slide defaults array right to right-align with
+        // _names array
+        LLSD::Integer offset = nparams - ndefaults;
+        // Fill rightmost _defaults entries from defaults, and mark them as
+        // filled
+        for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
+        {
+            _defaults[i + offset] = defaults[i];
+            _has_dft[i + offset] = 1;
+        }
+    }
+    else if (defaults.isMap())
+    {
+        // defaults is a map. Use it to populate the _defaults array.
+        LLSD bogus;
+        for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
+             mi != mend; ++mi)
+        {
+            IndexMap::const_iterator ixit(_indexes.find(mi->first));
+            if (ixit == _indexes.end())
+            {
+                bogus.append(mi->first);
+                continue;
+            }
+
+            LLSD::Integer pos = ixit->second;
+            // Store default value at that position in the _defaults array.
+            _defaults[pos] = mi->second;
+            // Don't forget to record the fact that we've filled this
+            // position.
+            _has_dft[pos] = 1;
+        }
+        if (bogus.size())
+        {
+            LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
+                                      << formatlist(bogus) << LL_ENDL;
+        }
+    }
+    else
+    {
+        LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
+                                  << defaults << LL_ENDL;
+    }
+}
+
+LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
+{
+    if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
+    {
+        LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
+                                  << argsmap << LL_ENDL;
+    }
+    // Initialize the args array. Indexing a non-const LLSD array grows it
+    // to appropriate size, but we don't want to resize this one on each
+    // new operation. Just make it as big as we need before we start
+    // stuffing values into it.
+    LLSD args(LLSD::emptyArray());
+    if (_defaults.size() == 0)
+    {
+        // If this function requires no arguments, fast exit. (Don't try to
+        // assign to args[-1].)
+        return args;
+    }
+    args[_defaults.size() - 1] = LLSD();
+
+    // Get a vector of chars to indicate holes. It's tempting to just scan
+    // for LLSD::isUndefined() values after filling the args array from
+    // the map, but it's plausible for caller to explicitly pass
+    // isUndefined() as the value of some parameter name. That's legal
+    // since isUndefined() has well-defined conversions (default value)
+    // for LLSD data types. So use a whole separate array for detecting
+    // holes. (Avoid std::vector<bool> which is known to be odd -- can we
+    // iterate?)
+    FilledVector filled(args.size());
+
+    if (argsmap.isArray())
+    {
+        // Fill args from array. If there are too many args in passed array,
+        // ignore the rest.
+        LLSD::Integer size(argsmap.size());
+        if (size > args.size())
+        {
+            // We don't just use std::min() because we want to sneak in this
+            // warning if caller passes too many args.
+            LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
+                                       << " params, ignoring last " << (size - args.size())
+                                       << " of passed " << size << ": " << argsmap << LL_ENDL;
+            size = args.size();
+        }
+        for (LLSD::Integer i(0); i < size; ++i)
+        {
+            // Copy the actual argument from argsmap
+            args[i] = argsmap[i];
+            // Note that it's been filled
+            filled[i] = 1;
+        }
+    }
+    else
+    {
+        // argsmap is in fact a map. Walk the map.
+        for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
+             mi != mend; ++mi)
+        {
+            // mi->first is a parameter-name string, with mi->second its
+            // value. Look up the name's position index in _indexes.
+            IndexMap::const_iterator ixit(_indexes.find(mi->first));
+            if (ixit == _indexes.end())
+            {
+                // Allow for a map containing more params than were passed in
+                // our names array. Caller typically receives a map containing
+                // the function name, cruft such as reqid, etc. Ignore keys
+                // not defined in _indexes.
+                LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
+                                            << mi->first << "=" << mi->second << LL_ENDL;
+                continue;
+            }
+            LLSD::Integer pos = ixit->second;
+            // Store the value at that position in the args array.
+            args[pos] = mi->second;
+            // Don't forget to record the fact that we've filled this
+            // position.
+            filled[pos] = 1;
+        }
+    }
+
+    // Fill any remaining holes from _defaults.
+    LLSD unfilled(LLSD::emptyArray());
+    for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
+    {
+        if (! filled[i])
+        {
+            // If there's no default value for this parameter, that's an
+            // error.
+            if (! _has_dft[i])
+            {
+                unfilled.append(_names[i]);
+            }
+            else
+            {
+                args[i] = _defaults[i];
+            }
+        }
+    }
+    // If any required args -- args without defaults -- were left unfilled
+    // by argsmap, that's a problem.
+    if (unfilled.size())
+    {
+        LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
+                                  << formatlist(unfilled) << " from " << argsmap << LL_ENDL;
+    }
+
+    // done
+    return args;
+}
+
+std::string LLSDArgsMapper::formatlist(const LLSD& list)
+{
+    std::ostringstream out;
+    const char* delim = "";
+    for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
+         li != lend; ++li)
+    {
+        out << delim << li->asString();
+        delim = ", ";
+    }
+    return out.str();
+}
 
 LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
     mDesc(desc),
 {
 }
 
+/**
+ * DispatchEntry subclass used for callables accepting(const LLSD&)
+ */
+struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
+{
+    LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
+        DispatchEntry(desc),
+        mFunc(func),
+        mRequired(required)
+    {}
+
+    Callable mFunc;
+    LLSD mRequired;
+
+    virtual void call(const std::string& desc, const LLSD& event) const
+    {
+        // Validate the syntax of the event itself.
+        std::string mismatch(llsd_matches(mRequired, event));
+        if (! mismatch.empty())
+        {
+            LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
+        }
+        // Event syntax looks good, go for it!
+        mFunc(event);
+    }
+
+    virtual LLSD addMetadata(LLSD meta) const
+    {
+        meta["required"] = mRequired;
+        return meta;
+    }
+};
+
+/**
+ * DispatchEntry subclass for passing LLSD to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
+{
+    ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
+        DispatchEntry(desc),
+        mInvoker(func)
+    {}
+
+    invoker_function mInvoker;
+
+    virtual void call(const std::string& desc, const LLSD& event) const
+    {
+        LLSDArgsSource src(desc, event);
+        mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
+    }
+};
+
+/**
+ * DispatchEntry subclass for dispatching LLSD::Array to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
+{
+    ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
+                             LLSD::Integer arity):
+        ParamsDispatchEntry(desc, func),
+        mArity(arity)
+    {}
+
+    LLSD::Integer mArity;
+
+    virtual LLSD addMetadata(LLSD meta) const
+    {
+        LLSD array(LLSD::emptyArray());
+        // Resize to number of arguments required
+        if (mArity)
+            array[mArity - 1] = LLSD();
+        llassert_always(array.size() == mArity);
+        meta["required"] = array;
+        return meta;
+    }
+};
+
+/**
+ * DispatchEntry subclass for dispatching LLSD::Map to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
+{
+    MapParamsDispatchEntry(const std::string& name, const std::string& desc,
+                           const invoker_function& func,
+                           const LLSD& params, const LLSD& defaults):
+        ParamsDispatchEntry(desc, func),
+        mMapper(name, params, defaults),
+        mRequired(LLSD::emptyMap())
+    {
+        // Build the set of all param keys, then delete the ones that are
+        // optional. What's left are the ones that are required.
+        for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
+             pi != pend; ++pi)
+        {
+            mRequired[pi->asString()] = LLSD();
+        }
+
+        if (defaults.isArray() || defaults.isUndefined())
+        {
+            // Right-align the params and defaults arrays.
+            LLSD::Integer offset = params.size() - defaults.size();
+            // Now the name of every defaults[i] is at params[i + offset].
+            for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
+            {
+                // Erase this optional param from mRequired.
+                mRequired.erase(params[i + offset].asString());
+                // Instead, make an entry in mOptional with the default
+                // param's name and value.
+                mOptional[params[i + offset].asString()] = defaults[i];
+            }
+        }
+        else if (defaults.isMap())
+        {
+            // if defaults is already a map, then it's already in the form we
+            // intend to deliver in metadata
+            mOptional = defaults;
+            // Just delete from mRequired every key appearing in mOptional.
+            for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
+                 mi != mend; ++mi)
+            {
+                mRequired.erase(mi->first);
+            }
+        }
+    }
+
+    LLSDArgsMapper mMapper;
+    LLSD mRequired;
+    LLSD mOptional;
+
+    virtual void call(const std::string& desc, const LLSD& event) const
+    {
+        // Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
+        // to base-class call() method.
+        ParamsDispatchEntry::call(desc, mMapper.map(event));
+    }
+
+    virtual LLSD addMetadata(LLSD meta) const
+    {
+        meta["required"] = mRequired;
+        meta["optional"] = mOptional;
+        return meta;
+    }
+};
+
+void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
+                                                    const std::string& desc,
+                                                    const invoker_function& invoker,
+                                                    LLSD::Integer arity)
+{
+    mDispatch.insert(
+        DispatchMap::value_type(name, DispatchMap::mapped_type(
+                                    new ArrayParamsDispatchEntry(desc, invoker, arity))));
+}
+
+void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
+                                                  const std::string& desc,
+                                                  const invoker_function& invoker,
+                                                  const LLSD& params,
+                                                  const LLSD& defaults)
+{
+    mDispatch.insert(
+        DispatchMap::value_type(name, DispatchMap::mapped_type(
+                                    new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
+}
+
 /// Register a callable by name
 void LLEventDispatcher::add(const std::string& name, const std::string& desc,
                             const Callable& callable, const LLSD& required)
 {
-    mDispatch.insert(DispatchMap::value_type(name,
-                                             DispatchMap::mapped_type(callable, desc, required)));
+    mDispatch.insert(
+        DispatchMap::value_type(name, DispatchMap::mapped_type(
+                                    new LLSDDispatchEntry(desc, callable, required))));
 }
 
 void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
 /// such callable exists, die with LL_ERRS.
 void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
 {
-    if (! attemptCall(name, event))
+    if (! try_call(name, event))
     {
         LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
                                      << "' not found" << LL_ENDL;
     // This could/should be implemented in terms of the two-arg overload.
     // However -- we can produce a more informative error message.
     std::string name(event[mKey]);
-    if (! attemptCall(name, event))
+    if (! try_call(name, event))
     {
         LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
                                      << " value '" << name << "'" << LL_ENDL;
     }
 }
 
-bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const
+bool LLEventDispatcher::try_call(const LLSD& event) const
+{
+    return try_call(event[mKey], event);
+}
+
+bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
 {
     DispatchMap::const_iterator found = mDispatch.find(name);
     if (found == mDispatch.end())
     {
-        // The reason we only return false, leaving it up to our caller to die
-        // with LL_ERRS, is that different callers have different amounts of
-        // available information.
         return false;
     }
-    // Found the name, so it's plausible to even attempt the call. But first,
-    // validate the syntax of the event itself.
-    std::string mismatch(llsd_matches(found->second.mRequired, event));
-    if (! mismatch.empty())
-    {
-        LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name
-                                     << "': bad request: " << mismatch << LL_ENDL;
-    }
-    // Event syntax looks good, go for it!
-    (found->second.mFunc)(event);
+    // Found the name, so it's plausible to even attempt the call.
+    found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
+                        event);
     return true;                    // tell caller we were able to call
 }
 
-LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const
-{
-    DispatchMap::const_iterator found = mDispatch.find(name);
-    if (found == mDispatch.end())
-    {
-        return Callable();
-    }
-    return found->second.mFunc;
-}
-
 LLSD LLEventDispatcher::getMetadata(const std::string& name) const
 {
     DispatchMap::const_iterator found = mDispatch.find(name);
     }
     LLSD meta;
     meta["name"] = name;
-    meta["desc"] = found->second.mDesc;
-    meta["required"] = found->second.mRequired;
-    return meta;
+    meta["desc"] = found->second->mDesc;
+    return found->second->addMetadata(meta);
 }
 
 LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
     (*this)(event);
     return false;
 }
+
+LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
+    mDesc(desc)
+{}
+

indra/llcommon/lleventdispatcher.h

  * 
  * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  * $/LicenseInfo$
+ *
+ * The invoker machinery that constructs a boost::fusion argument list for use
+ * with boost::fusion::invoke() is derived from
+ * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
+ * whose license information is copied below:
+ *
+ * "(C) Copyright Tobias Schwinger
+ *
+ * Use modification and distribution are subject to the boost Software License,
+ * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
  */
 
 #if ! defined(LL_LLEVENTDISPATCHER_H)
 #define LL_LLEVENTDISPATCHER_H
 
+// nil is too generic a term to be allowed to be a global macro. In
+// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
+// namespace) that a global 'nil' macro breaks badly.
+#if defined(nil)
+// Capture the value of the macro 'nil', hoping int is an appropriate type.
+static const int nil_(nil);
+// Now forget the macro.
+#undef nil
+// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
+// defined const 'nil_'. Make it static since otherwise it produces duplicate-
+// symbol link errors later.
+static const int& nil(nil_);
+#endif
+
 #include <string>
-#include <map>
+#include <boost/shared_ptr.hpp>
 #include <boost/function.hpp>
 #include <boost/bind.hpp>
 #include <boost/iterator/transform_iterator.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/function_types/is_nonmember_callable_builtin.hpp>
+#include <boost/function_types/parameter_types.hpp>
+#include <boost/function_types/function_arity.hpp>
+#include <boost/type_traits/remove_cv.hpp>
+#include <boost/type_traits/remove_reference.hpp>
+#include <boost/fusion/include/push_back.hpp>
+#include <boost/fusion/include/cons.hpp>
+#include <boost/fusion/include/invoke.hpp>
+#include <boost/mpl/begin.hpp>
+#include <boost/mpl/end.hpp>
+#include <boost/mpl/next.hpp>
+#include <boost/mpl/deref.hpp>
 #include <typeinfo>
 #include "llevents.h"
+#include "llsdutil.h"
 
 class LLSD;
 
     LLEventDispatcher(const std::string& desc, const std::string& key);
     virtual ~LLEventDispatcher();
 
-    /// Accept any C++ callable, typically a boost::bind() expression
+    /// @name Register functions accepting(const LLSD&)
+    //@{
+
+    /// Accept any C++ callable with the right signature, typically a
+    /// boost::bind() expression
     typedef boost::function<void(const LLSD&)> Callable;
 
     /**
-     * Register a @a callable by @a name. The optional @a required parameter
-     * is used to validate the structure of each incoming event (see
+     * Register a @a callable by @a name. The passed @a callable accepts a
+     * single LLSD value and uses it in any way desired, e.g. extract
+     * parameters and call some other function. The optional @a required
+     * parameter is used to validate the structure of each incoming event (see
      * llsd_matches()).
      */
     void add(const std::string& name,
              const LLSD& required=LLSD());
 
     /**
+     * The case of a free function (or static method) accepting(const LLSD&)
+     * could also be intercepted by the arbitrary-args overload below. Ensure
+     * that it's directed to the Callable overload above instead.
+     */
+    void add(const std::string& name,
+             const std::string& desc,
+             void (*f)(const LLSD&),
+             const LLSD& required=LLSD())
+    {
+        add(name, desc, Callable(f), required);
+    }
+
+    /**
      * Special case: a subclass of this class can pass an unbound member
-     * function pointer without explicitly specifying the
-     * <tt>boost::bind()</tt> expression.
+     * function pointer (of an LLEventDispatcher subclass) without explicitly
+     * specifying the <tt>boost::bind()</tt> expression. The passed @a method
+     * accepts a single LLSD value, presumably containing other parameters.
      */
     template <class CLASS>
     void add(const std::string& name,
         addMethod<CLASS>(name, desc, method, required);
     }
 
-    /// Overload for both const and non-const methods
+    /// Overload for both const and non-const methods. The passed @a method
+    /// accepts a single LLSD value, presumably containing other parameters.
     template <class CLASS>
     void add(const std::string& name,
              const std::string& desc,
         addMethod<CLASS>(name, desc, method, required);
     }
 
-    /// Convenience: for LLEventDispatcher, not every callable needs a
-    /// documentation string.
-    template <typename CALLABLE>
-    void add(const std::string& name,
-             CALLABLE callable,
-             const LLSD& required=LLSD())
-    {
-        add(name, "", callable, required);
-    }
+    //@}
+
+    /// @name Register functions with arbitrary param lists
+    //@{
+
+    /**
+     * Register a free function with arbitrary parameters. (This also works
+     * for static class methods.)
+     *
+     * @note This supports functions with up to about 6 parameters -- after
+     * that you start getting dismaying compile errors in which
+     * boost::fusion::joint_view is mentioned a surprising number of times.
+     *
+     * When calling this name, pass an LLSD::Array. Each entry in turn will be
+     * converted to the corresponding parameter type using LLSDParam.
+     */
+    template<typename Function>
+    typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
+                               >::type add(const std::string& name,
+                                           const std::string& desc,
+                                           Function f);
+
+    /**
+     * Register a nonstatic class method with arbitrary parameters.
+     *
+     * @note This supports functions with up to about 6 parameters -- after
+     * that you start getting dismaying compile errors in which
+     * boost::fusion::joint_view is mentioned a surprising number of times.
+     *
+     * To cover cases such as a method on an LLSingleton we don't yet want to
+     * instantiate, instead of directly storing an instance pointer, accept a
+     * nullary callable returning a pointer/reference to the desired class
+     * instance. If you already have an instance in hand,
+     * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
+     * produce suitable callables.
+     *
+     * When calling this name, pass an LLSD::Array. Each entry in turn will be
+     * converted to the corresponding parameter type using LLSDParam.
+     */
+    template<typename Method, typename InstanceGetter>
+    typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
+                               >::type add(const std::string& name,
+                                           const std::string& desc,
+                                           Method f,
+                                           const InstanceGetter& getter);
+
+    /**
+     * Register a free function with arbitrary parameters. (This also works
+     * for static class methods.)
+     *
+     * @note This supports functions with up to about 6 parameters -- after
+     * that you start getting dismaying compile errors in which
+     * boost::fusion::joint_view is mentioned a surprising number of times.
+     *
+     * Pass an LLSD::Array of parameter names, and optionally another
+     * LLSD::Array of default parameter values, a la LLSDArgsMapper.
+     *
+     * When calling this name, pass an LLSD::Map. We will internally generate
+     * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
+     * to the corresponding parameter type using LLSDParam.
+     */
+    template<typename Function>
+    typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
+                               >::type add(const std::string& name,
+                                           const std::string& desc,
+                                           Function f,
+                                           const LLSD& params,
+                                           const LLSD& defaults=LLSD());
+
+    /**
+     * Register a nonstatic class method with arbitrary parameters.
+     *
+     * @note This supports functions with up to about 6 parameters -- after
+     * that you start getting dismaying compile errors in which
+     * boost::fusion::joint_view is mentioned a surprising number of times.
+     *
+     * To cover cases such as a method on an LLSingleton we don't yet want to
+     * instantiate, instead of directly storing an instance pointer, accept a
+     * nullary callable returning a pointer/reference to the desired class
+     * instance. If you already have an instance in hand,
+     * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
+     * produce suitable callables.
+     *
+     * Pass an LLSD::Array of parameter names, and optionally another
+     * LLSD::Array of default parameter values, a la LLSDArgsMapper.
+     *
+     * When calling this name, pass an LLSD::Map. We will internally generate
+     * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
+     * to the corresponding parameter type using LLSDParam.
+     */
+    template<typename Method, typename InstanceGetter>
+    typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
+                               >::type add(const std::string& name,
+                                           const std::string& desc,
+                                           Method f,
+                                           const InstanceGetter& getter,
+                                           const LLSD& params,
+                                           const LLSD& defaults=LLSD());
+
+    //@}    
 
     /// Unregister a callable
     bool remove(const std::string& name);
     /// the @a required prototype specified at add() time, die with LL_ERRS.
     void operator()(const std::string& name, const LLSD& event) const;
 
+    /// Call a registered callable with an explicitly-specified name and
+    /// return <tt>true</tt>. If no such callable exists, return
+    /// <tt>false</tt>. If the @a event fails to match the @a required
+    /// prototype specified at add() time, die with LL_ERRS.
+    bool try_call(const std::string& name, const LLSD& event) const;
+
     /// Extract the @a key value from the incoming @a event, and call the
     /// callable whose name is specified by that map @a key. If no such
     /// callable exists, die with LL_ERRS. If the @a event fails to match the
     /// @a required prototype specified at add() time, die with LL_ERRS.
     void operator()(const LLSD& event) const;
 
+    /// Extract the @a key value from the incoming @a event, call the callable
+    /// whose name is specified by that map @a key and return <tt>true</tt>.
+    /// If no such callable exists, return <tt>false</tt>. If the @a event
+    /// fails to match the @a required prototype specified at add() time, die
+    /// with LL_ERRS.
+    bool try_call(const LLSD& event) const;
+
     /// @name Iterate over defined names
     //@{
     typedef std::pair<std::string, std::string> NameDesc;
 private:
     struct DispatchEntry
     {
-        DispatchEntry(const Callable& func, const std::string& desc, const LLSD& required):
-            mFunc(func),
-            mDesc(desc),
-            mRequired(required)
-        {}
-        Callable mFunc;
+        DispatchEntry(const std::string& desc);
+        virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
+
         std::string mDesc;
-        LLSD mRequired;
+
+        virtual void call(const std::string& desc, const LLSD& event) const = 0;
+        virtual LLSD addMetadata(LLSD) const = 0;
     };
-    typedef std::map<std::string, DispatchEntry> DispatchMap;
+    // Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
+    // wants its value type to be "clonable," even just to dereference an
+    // iterator. I don't want to clone entries -- if I have to copy an entry
+    // around, I want it to continue pointing to the same DispatchEntry
+    // subclass object. However, I definitely want DispatchMap to destroy
+    // DispatchEntry if no references are outstanding at the time an entry is
+    // removed. This looks like a job for boost::shared_ptr.
+    typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
 
 public:
     /// We want the flexibility to redefine what data we store per name,
     }
     //@}
 
-    /// Fetch the Callable for the specified name. If no such name was
-    /// registered, return an empty() Callable.
-    Callable get(const std::string& name) const;
-
     /// Get information about a specific Callable
     LLSD getMetadata(const std::string& name) const;
 
         }
     }
     void addFail(const std::string& name, const std::string& classname) const;
-    /// try to dispatch, return @c true if success
-    bool attemptCall(const std::string& name, const LLSD& event) const;
 
     std::string mDesc, mKey;
     DispatchMap mDispatch;
 
     static NameDesc makeNameDesc(const DispatchMap::value_type& item)
     {
-        return NameDesc(item.first, item.second.mDesc);
+        return NameDesc(item.first, item.second->mDesc);
+    }
+
+    struct LLSDDispatchEntry;
+    struct ParamsDispatchEntry;
+    struct ArrayParamsDispatchEntry;
+    struct MapParamsDispatchEntry;
+
+    // Step 2 of parameter analysis. Instantiating invoker<some_function_type>
+    // implicitly sets its From and To parameters to the (compile time) begin
+    // and end iterators over that function's parameter types.
+    template< typename Function
+              , class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
+              , class To   = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
+              >
+    struct invoker;
+
+    // deliver LLSD arguments one at a time
+    typedef boost::function<LLSD()> args_source;
+    // obtain args from an args_source to build param list and call target
+    // function
+    typedef boost::function<void(const args_source&)> invoker_function;
+
+    template <typename Function>
+    invoker_function make_invoker(Function f);
+    template <typename Method, typename InstanceGetter>
+    invoker_function make_invoker(Method f, const InstanceGetter& getter);
+    void addArrayParamsDispatchEntry(const std::string& name,
+                                     const std::string& desc,
+                                     const invoker_function& invoker,
+                                     LLSD::Integer arity);
+    void addMapParamsDispatchEntry(const std::string& name,
+                                   const std::string& desc,
+                                   const invoker_function& invoker,
+                                   const LLSD& params,
+                                   const LLSD& defaults);
+};
+
+/*****************************************************************************
+*   LLEventDispatcher template implementation details
+*****************************************************************************/
+// Step 3 of parameter analysis, the recursive case.
+template<typename Function, class From, class To>
+struct LLEventDispatcher::invoker
+{
+    template<typename T>
+    struct remove_cv_ref
+        : boost::remove_cv< typename boost::remove_reference<T>::type >
+    { };
+
+    // apply() accepts an arbitrary boost::fusion sequence as args. It
+    // examines the next parameter type in the parameter-types sequence
+    // bounded by From and To, obtains the next LLSD object from the passed
+    // args_source and constructs an LLSDParam of appropriate type to try
+    // to convert the value. It then recurs with the next parameter-types
+    // iterator, passing the args sequence thus far.
+    template<typename Args>
+    static inline
+    void apply(Function func, const args_source& argsrc, Args const & args)
+    {
+        typedef typename boost::mpl::deref<From>::type arg_type;
+        typedef typename boost::mpl::next<From>::type next_iter_type;
+        typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
+
+        invoker<Function, next_iter_type, To>::apply
+        ( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
+    }
+
+    // Special treatment for instance (first) parameter of a non-static member
+    // function. Accept the instance-getter callable, calling that to produce
+    // the first args value. Since we know we're at the top of the recursion
+    // chain, we need not also require a partial args sequence from our caller.
+    template <typename InstanceGetter>
+    static inline
+    void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
+    {
+        typedef typename boost::mpl::next<From>::type next_iter_type;
+
+        // Instead of grabbing the first item from argsrc and making an
+        // LLSDParam of it, call getter() and pass that as the instance param.
+        invoker<Function, next_iter_type, To>::apply
+        ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
     }
 };
 
+// Step 4 of parameter analysis, the leaf case. When the general
+// invoker<Function, From, To> logic has advanced From until it matches To,
+// the compiler will pick this template specialization.
+template<typename Function, class To>
+struct LLEventDispatcher::invoker<Function,To,To>
+{
+    // the argument list is complete, now call the function
+    template<typename Args>
+    static inline
+    void apply(Function func, const args_source&, Args const & args)
+    {
+        boost::fusion::invoke(func, args);
+    }
+};
+
+template<typename Function>
+typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
+{
+    // Construct an invoker_function, a callable accepting const args_source&.
+    // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
+    // caller's LLSD::Array.
+    addArrayParamsDispatchEntry(name, desc, make_invoker(f),
+                                boost::function_types::function_arity<Function>::value);
+}
+
+template<typename Method, typename InstanceGetter>
+typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
+                       const InstanceGetter& getter)
+{
+    // Subtract 1 from the compile-time arity because the getter takes care of
+    // the first parameter. We only need (arity - 1) additional arguments.
+    addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
+                                boost::function_types::function_arity<Method>::value - 1);
+}
+
+template<typename Function>
+typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
+                       const LLSD& params, const LLSD& defaults)
+{
+    // See comments for previous is_nonmember_callable_builtin add().
+    addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
+}
+
+template<typename Method, typename InstanceGetter>
+typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
+                       const InstanceGetter& getter,
+                       const LLSD& params, const LLSD& defaults)
+{
+    addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
+}
+
+template <typename Function>
+LLEventDispatcher::invoker_function
+LLEventDispatcher::make_invoker(Function f)
+{
+    // Step 1 of parameter analysis, the top of the recursion. Passing a
+    // suitable f (see add()'s enable_if condition) to this method causes it
+    // to infer the function type; specifying that function type to invoker<>
+    // causes it to fill in the begin/end MPL iterators over the function's
+    // list of parameter types.
+    // While normally invoker::apply() could infer its template type from the
+    // boost::fusion::nil parameter value, here we must be explicit since
+    // we're boost::bind()ing it rather than calling it directly.
+    return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
+                       f,
+                       _1,
+                       boost::fusion::nil());
+}
+
+template <typename Method, typename InstanceGetter>
+LLEventDispatcher::invoker_function
+LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
+{
+    // Use invoker::method_apply() to treat the instance (first) arg specially.
+    return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
+                       f,
+                       _1,
+                       getter);
+}
+
+/*****************************************************************************
+*   LLDispatchListener
+*****************************************************************************/
 /**
  * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
  * that contains (or derives from) LLDispatchListener need only specify the

indra/llcommon/llevents.cpp

     }
     response["reqid"] = mReqid;
 }
+
+bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
+{
+    // Copy 'reply' to modify it.
+    LLSD newreply(reply);
+    // Get the ["reqid"] element from request
+    LLReqID reqID(request);
+    // and copy it to 'newreply'.
+    reqID.stamp(newreply);
+    // Send reply on LLEventPump named in request[replyKey]. Don't forget to
+    // send the modified 'newreply' instead of the original 'reply'.
+    return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
+}

indra/llcommon/llevents.h

 };
 
 /**
+ * Conventionally send a reply to a request event.
+ *
+ * @a reply is the LLSD reply event to send
+ * @a request is the corresponding LLSD request event
+ * @a replyKey is the key in the @a request event, conventionally ["reply"],
+ * whose value is the name of the LLEventPump on which to send the reply.
+ *
+ * Before sending the reply event, sendReply() copies the ["reqid"] item from
+ * the request to the reply.
+ */
+LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request,
+                             const std::string& replyKey="reply");
+
+/**
  * Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We
  * provide virtual @c accept_xxx() methods, customization points allowing a
  * subclass access to certain data visible at LLEventPump::listen() time.

indra/llcommon/llinstancetracker.cpp

 // external library headers
 // other Linden headers
 
-// llinstancetracker.h is presently header-only. This file exists only because our CMake
-// test macro ADD_BUILD_TEST requires it.
-int dummy = 0;
+//static 
+void * & LLInstanceTrackerBase::getInstances(std::type_info const & info)
+{
+	static std::map<std::string, void *> instances;
+
+	std::string k = info.name();
+	if(instances.find(k) == instances.end())
+	{
+		instances[k] = NULL;
+	}
+
+	return instances[k];
+}
+

indra/llcommon/llinstancetracker.h

 #include <boost/iterator/transform_iterator.hpp>
 #include <boost/iterator/indirect_iterator.hpp>
 
+class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable
+{
+	protected:
+		static void * & getInstances(std::type_info const & info);
+};
+
 /// This mix-in class adds support for tracking all instances of the specified class parameter T
 /// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup
 /// If KEY is not provided, then instances are stored in a simple set
 /// @NOTE: see explicit specialization below for default KEY==T* case
 template<typename T, typename KEY = T*>
-class LLInstanceTracker : boost::noncopyable
+class LLInstanceTracker : public LLInstanceTrackerBase
 {
 	typedef typename std::map<KEY, T*> InstanceMap;
+	typedef LLInstanceTracker<T, KEY> MyT;
 	typedef boost::function<const KEY&(typename InstanceMap::value_type&)> KeyGetter;
 	typedef boost::function<T*(typename InstanceMap::value_type&)> InstancePtrGetter;
 public:
 
     static InstanceMap& getMap_()
     {
-        if (! sInstances)
+		void * & instances = getInstances(typeid(MyT));
+        if (! instances)
         {
-            sInstances = new InstanceMap;
+            instances = new InstanceMap;
         }
-        return *sInstances;
+        return * static_cast<InstanceMap*>(instances);
     }
 
 private:
 
 	KEY mKey;
-	static InstanceMap* sInstances;
 };
 
 /// explicit specialization for default case where KEY is T*
 /// use a simple std::set<T*>
 template<typename T>
-class LLInstanceTracker<T, T*>
+class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase
 {
 	typedef typename std::set<T*> InstanceSet;
+	typedef LLInstanceTracker<T, T*> MyT;
 public:
 	/// Dereferencing key_iter gives you a T* (since T* is the key)
 	typedef typename InstanceSet::iterator key_iter;
 
 	static InstanceSet& getSet_()
 	{
-		if (! sInstances)
+		void * & instances = getInstances(typeid(MyT));
+		if (! instances)
 		{
-			sInstances = new InstanceSet;
+			instances = new InstanceSet;
 		}
-		return *sInstances;
+		return * static_cast<InstanceSet *>(instances);
 	}
 
-	static InstanceSet* sInstances;
 	static S32 sIterationNestDepth;
 };
 
-template <typename T, typename KEY> typename LLInstanceTracker<T, KEY>::InstanceMap* LLInstanceTracker<T, KEY>::sInstances = NULL;
-template <typename T> typename LLInstanceTracker<T, T*>::InstanceSet* LLInstanceTracker<T, T*>::sInstances = NULL;
 template <typename T> S32 LLInstanceTracker<T, T*>::sIterationNestDepth = 0;
 
 #endif

indra/llcommon/llsdutil.cpp

 
 #include "llsdserialize.h"
 #include "stringize.h"
+#include "is_approx_equal_fraction.h"
 
 #include <map>
 #include <set>
     return match_types(prototype.type(), TypeVector(), data.type(), pfx);
 }
 
-bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
+bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits)
 {
     // We're comparing strict equality of LLSD representation rather than
     // performing any conversions. So if the types aren't equal, the LLSD
         // Both are TypeUndefined. There's nothing more to know.
         return true;
 
+    case LLSD::TypeReal:
+        // This is where the 'bits' argument comes in handy. If passed
+        // explicitly, it means to use is_approx_equal_fraction() to compare.
+        if (bits >= 0)
+        {
+            return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits);
+        }
+        // Otherwise we compare bit representations, and the usual caveats
+        // about comparing floating-point numbers apply. Omitting 'bits' when
+        // comparing Real values is only useful when we expect identical bit
+        // representation for a given Real value, e.g. for integer-valued
+        // Reals.
+        return (lhs.asReal() == rhs.asReal());
+
 #define COMPARE_SCALAR(type)                                    \
     case LLSD::Type##type:                                      \
         /* LLSD::URI has operator!=() but not operator==() */   \
 
     COMPARE_SCALAR(Boolean);
     COMPARE_SCALAR(Integer);
-    // The usual caveats about comparing floating-point numbers apply. This is
-    // only useful when we expect identical bit representation for a given
-    // Real value, e.g. for integer-valued Reals.
-    COMPARE_SCALAR(Real);
     COMPARE_SCALAR(String);
     COMPARE_SCALAR(UUID);
     COMPARE_SCALAR(Date);
         for ( ; lai != laend && rai != raend; ++lai, ++rai)
         {
             // If any one array element is unequal, the arrays are unequal.
-            if (! llsd_equals(*lai, *rai))
+            if (! llsd_equals(*lai, *rai, bits))
                 return false;
         }
         // Here we've reached the end of one or the other array. They're equal
             if (rhskeys.erase(lmi->first) != 1)
                 return false;
             // Both maps have the current key. Compare values.
-            if (! llsd_equals(lmi->second, rhs[lmi->first]))
+            if (! llsd_equals(lmi->second, rhs[lmi->first], bits))
                 return false;
         }
         // We've now established that all the lhs keys have equal values in
         // We expect that every possible type() value is specifically handled
         // above. Failing to extend this switch to support a new LLSD type is
         // an error that must be brought to the coder's attention.
-        LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): "
+        LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): "
             "unknown type " << lhs.type() << LL_ENDL;
         return false;               // pacify the compiler
     }

indra/llcommon/llsdutil.h

  */
 LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
 
-/// Deep equality
-LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs);
+/// Deep equality. If you want to compare LLSD::Real values for approximate
+/// equality rather than bitwise equality, pass @a bits as for
+/// is_approx_equal_fraction().
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits=-1);
 
 // Simple function to copy data out of input & output iterators if
 // there is no need for casting.
 	return dest;
 }
 
+/*****************************************************************************
+*   LLSDArray
+*****************************************************************************/
+/**
+ * Construct an LLSD::Array inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDArray("text")(17)(3.14));
+ * @endcode
+ *
+ * For completeness, LLSDArray() with no args constructs an empty array, so
+ * <tt>LLSDArray()("text")(17)(3.14)</tt> produces an array equivalent to the
+ * above. But for most purposes, LLSD() is already equivalent to an empty
+ * array, and if you explicitly want an empty isArray(), there's
+ * LLSD::emptyArray(). However, supporting a no-args LLSDArray() constructor
+ * follows the principle of least astonishment.
+ */
+class LLSDArray
+{
+public:
+    LLSDArray():
+        _data(LLSD::emptyArray())
+    {}
+
+    /**
+     * Need an explicit copy constructor. Consider the following:
+     *
+     * @code
+     * LLSD array_of_arrays(LLSDArray(LLSDArray(17)(34))
+     *                               (LLSDArray("x")("y")));
+     * @endcode
+     *
+     * The coder intends to construct [[17, 34], ["x", "y"]].
+     *
+     * With the compiler's implicit copy constructor, s/he gets instead
+     * [17, 34, ["x", "y"]].
+     *
+     * The expression LLSDArray(17)(34) constructs an LLSDArray with those two
+     * values. The reader assumes it should be converted to LLSD, as we always
+     * want with LLSDArray, before passing it to the @em outer LLSDArray
+     * constructor! This copy constructor makes that happen.
+     */
+    LLSDArray(const LLSDArray& inner):
+        _data(LLSD::emptyArray())
+    {
+        _data.append(inner);
+    }
+
+    LLSDArray(const LLSD& value):
+        _data(LLSD::emptyArray())
+    {
+        _data.append(value);
+    }
+
+    LLSDArray& operator()(const LLSD& value)
+    {
+        _data.append(value);
+        return *this;
+    }
+
+    operator LLSD() const { return _data; }
+    LLSD get() const { return _data; }
+
+private:
+    LLSD _data;
+};
+
+/*****************************************************************************
+*   LLSDMap
+*****************************************************************************/
+/**
+ * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
+ * @endcode
+ *
+ * For completeness, LLSDMap() with no args constructs an empty map, so
+ * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
+ * equivalent to the above. But for most purposes, LLSD() is already
+ * equivalent to an empty map, and if you explicitly want an empty isMap(),
+ * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
+ * constructor follows the principle of least astonishment.
+ */
+class LLSDMap
+{
+public:
+    LLSDMap():
+        _data(LLSD::emptyMap())
+    {}
+    LLSDMap(const LLSD::String& key, const LLSD& value):
+        _data(LLSD::emptyMap())
+    {
+        _data[key] = value;
+    }
+
+    LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
+    {
+        _data[key] = value;
+        return *this;
+    }
+
+    operator LLSD() const { return _data; }
+    LLSD get() const { return _data; }
+
+private:
+    LLSD _data;
+};
+
+/*****************************************************************************
+*   LLSDParam
+*****************************************************************************/
+/**
+ * LLSDParam is a customization point for passing LLSD values to function
+ * parameters of more or less arbitrary type. LLSD provides a small set of
+ * native conversions; but if a generic algorithm explicitly constructs an
+ * LLSDParam object in the function's argument list, a consumer can provide
+ * LLSDParam specializations to support more different parameter types than
+ * LLSD's native conversions.
+ *
+ * Usage:
+ *
+ * @code
+ * void somefunc(const paramtype&);
+ * ...
+ * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
+ * @endcode
+ */
+template <typename T>
+class LLSDParam
+{
+public:
+    /**
+     * Default implementation converts to T on construction, saves converted
+     * value for later retrieval
+     */
+    LLSDParam(const LLSD& value):
+        _value(value)
+    {}
+
+    operator T() const { return _value; }
+
+private:
+    T _value;
+};
+
+/**
+ * Turns out that several target types could accept an LLSD param using any of
+ * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
+ * std::string. Therefore, the compiler can't decide which LLSD conversion
+ * operator to choose, even though to us it seems obvious. But that's okay, we
+ * can specialize LLSDParam for such target types, explicitly specifying the
+ * desired conversion -- that's part of what LLSDParam is all about. Turns out
+ * we have to do that enough to make it worthwhile generalizing. Use a macro
+ * because I need to specify one of the asReal, etc., explicit conversion
+ * methods as well as a type. If I'm overlooking a clever way to implement
+ * that using a template instead, feel free to reimplement.
+ */
+#define LLSDParam_for(T, AS)                    \
+template <>                                     \
+class LLSDParam<T>                              \
+{                                               \
+public:                                         \
+    LLSDParam(const LLSD& value):               \
+        _value(value.AS())                      \
+    {}                                          \
+                                                \
+    operator T() const { return _value; }       \
+                                                \
+private:                                        \
+    T _value;                                   \
+}
+
+LLSDParam_for(float,        asReal);
+LLSDParam_for(LLUUID,       asUUID);
+LLSDParam_for(LLDate,       asDate);
+LLSDParam_for(LLURI,        asURI);
+LLSDParam_for(LLSD::Binary, asBinary);
+
+/**
+ * LLSDParam<const char*> is an example of the kind of conversion you can
+ * support with LLSDParam beyond native LLSD conversions. Normally you can't
+ * pass an LLSD object to a function accepting const char* -- but you can
+ * safely pass an LLSDParam<const char*>(yourLLSD).
+ */
+template <>
+class LLSDParam<const char*>
+{
+private:
+    // The difference here is that we store a std::string rather than a const
+    // char*. It's important that the LLSDParam object own the std::string.
+    std::string _value;
+    // We don't bother storing the incoming LLSD object, but we do have to
+    // distinguish whether _value is an empty string because the LLSD object
+    // contains an empty string or because it's isUndefined().
+    bool _undefined;
+
+public:
+    LLSDParam(const LLSD& value):
+        _value(value),
+        _undefined(value.isUndefined())
+    {}
+
+    // The const char* we retrieve is for storage owned by our _value member.
+    // That's how we guarantee that the const char* is valid for the lifetime
+    // of this LLSDParam object. Constructing your LLSDParam in the argument
+    // list should ensure that the LLSDParam object will persist for the
+    // duration of the function call.
+    operator const char*() const
+    {
+        if (_undefined)
+        {
+            // By default, an isUndefined() LLSD object's asString() method
+            // will produce an empty string. But for a function accepting
+            // const char*, it's often important to be able to pass NULL, and
+            // isUndefined() seems like the best way. If you want to pass an
+            // empty string, you can still pass LLSD(""). Without this special
+            // case, though, no LLSD value could pass NULL.
+            return NULL;
+        }
+        return _value.c_str();
+    }
+};
+
+namespace llsd
+{
+
+/*****************************************************************************
+*   BOOST_FOREACH() helpers for LLSD
+*****************************************************************************/
+/// Usage: BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+    inArray(const LLSD& array):
+        _array(array)
+    {}
+
+    typedef LLSD::array_const_iterator const_iterator;
+    typedef LLSD::array_iterator iterator;
+
+    iterator begin() { return _array.beginArray(); }
+    iterator end()   { return _array.endArray(); }
+    const_iterator begin() const { return _array.beginArray(); }
+    const_iterator end()   const { return _array.endArray(); }
+
+private:
+    LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
+
+/// Usage: BOOST_FOREACH([const] MapEntry& e, inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+    inMap(const LLSD& map):
+        _map(map)
+    {}
+
+    typedef LLSD::map_const_iterator const_iterator;
+    typedef LLSD::map_iterator iterator;
+
+    iterator begin() { return _map.beginMap(); }
+    iterator end()   { return _map.endMap(); }
+    const_iterator begin() const { return _map.beginMap(); }
+    const_iterator end()   const { return _map.endMap(); }
+
+private:
+    LLSD _map;
+};
+
+} // namespace llsd
+
 #endif // LL_LLSDUTIL_H

indra/llcommon/llversionviewer.h

 
 const S32 LL_VERSION_MAJOR = 2;
 const S32 LL_VERSION_MINOR = 6;
-const S32 LL_VERSION_PATCH = 3;
+const S32 LL_VERSION_PATCH = 5;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";

indra/llcommon/tests/lleventdispatcher_test.cpp

+/**
+ * @file   lleventdispatcher_test.cpp
+ * @author Nat Goodspeed
+ * @date   2011-01-20
+ * @brief  Test for lleventdispatcher.
+ * 
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventdispatcher.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llsd.h"
+#include "llsdutil.h"
+#include "stringize.h"
+#include "tests/wrapllerrs.h"
+
+#include <map>
+#include <string>
+#include <stdexcept>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/range.hpp>
+#include <boost/foreach.hpp>
+#define foreach BOOST_FOREACH
+
+#include <boost/lambda/lambda.hpp>
+
+#include <iostream>
+#include <iomanip>
+
+using boost::lambda::constant;
+using boost::lambda::constant_ref;
+using boost::lambda::var;
+
+using namespace llsd;
+
+/*****************************************************************************
+*   Output control
+*****************************************************************************/
+#ifdef DEBUG_ON
+using std::cout;
+#else
+static std::ostringstream cout;
+#endif
+
+/*****************************************************************************
+*   Example data, functions, classes
+*****************************************************************************/
+// We don't need a whole lot of different arbitrary-params methods, just (no |
+// (const LLSD&) | arbitrary) args (function | static method | non-static
+// method), where 'arbitrary' is (every LLSD datatype + (const char*)).
+// But we need to register each one under different names for the different
+// registration styles. Don't forget LLEventDispatcher subclass methods(const
+// LLSD&).
+
+// However, the number of target parameter conversions we want to try exceeds
+// boost::fusion::invoke()'s supported parameter-list size. Break out two
+// different lists.
+#define NPARAMSa bool b, int i, float f, double d, const char* cp
+#define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \
+                 const LLURI& uri, const std::vector<U8>& bin
+#define NARGSa   b, i, f, d, cp
+#define NARGSb   s, uuid, date, uri, bin
+