/*
 * MarshalOps.cs --
 *
 * Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: $
 */

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

#if NATIVE
using System.Security;
#endif

#if NATIVE && !NET_40
using System.Security.Permissions;
#endif

using Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Components.Public.Delegates;
using Eagle._Constants;
using Eagle._Containers.Private;
using Eagle._Containers.Public;
using Eagle._Interfaces.Private;
using Eagle._Interfaces.Public;
using DISPPARAMS = System.Runtime.InteropServices.ComTypes.DISPPARAMS;
using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO;

namespace Eagle._Components.Private
{
#if NATIVE
#if NET_40
    [SecurityCritical()]
#else
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
#endif
#endif
    [ObjectId("a6ba3c2e-1324-4e38-8b8b-eb563a71f2cc")]
    internal static class MarshalOps
    {
        private const char ParameterDelimiter = Characters.OpenBracket;

        internal const BindingFlags EnumFieldBindingFlags =
            BindingFlags.Static | BindingFlags.Public;

        internal const MemberTypes UnsafeObjectMemberTypes =
            MemberTypes.Constructor | MemberTypes.Event |
            MemberTypes.TypeInfo | MemberTypes.Custom |
            MemberTypes.NestedType;

        internal const BindingFlags UnsafeObjectBindingFlags =
            BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

        internal const BindingFlags NestedObjectBindingFlags =
            BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod |
            BindingFlags.GetField | BindingFlags.GetProperty;

        internal const BindingFlags LooseMethodBindingFlags =
            BindingFlags.IgnoreCase | BindingFlags.Instance |
            BindingFlags.Static | BindingFlags.Public |
            BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod;

        internal const BindingFlags HostInfoBindingFlags =
            BindingFlags.Instance | BindingFlags.Public |
            BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod |
            BindingFlags.GetField | BindingFlags.GetProperty;

#if !MONO
        private const string RuntimeAssemblyTypeName = "System.Reflection.RuntimeAssembly";
        private const string EnumerateCacheMethodName = "EnumerateCache";
#endif

#if !NET_40 && !MONO && DEAD_CODE
        //
        // NOTE: This is used only by the "EnumOps.GetEnumNamesAndValuesFast" method.
        //
        internal const string GetEnumValuesMethodName = "InternalGetEnumValues";
#endif

        private const string ParameterNamePrefix = "parameter";

        internal const string ImplicitOperatorMethodName = "op_Implicit";
        internal const string ExplicitOperatorMethodName = "op_Explicit";

        private static readonly StringList AccessorPrefixes = new StringList(new string[] {
            "get_", "set_"
        });

        private const int MaximumTypeLevels = 10;

        ///////////////////////////////////////////////////////////////////////////////////////////////

        internal const BindingFlags PublicStaticMethodBindingFlags = BindingFlags.Static |
            BindingFlags.Public | BindingFlags.InvokeMethod;

        internal const BindingFlags PrivateStaticGetFieldBindingFlags = BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.GetField;

        internal const BindingFlags PrivateStaticSetFieldBindingFlags = BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.SetField;

#if CONSOLE
        internal const BindingFlags PrivateStaticGetPropertyBindingFlags = BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.GetProperty;

        internal const BindingFlags PublicStaticGetPropertyBindingFlags = BindingFlags.Static |
            BindingFlags.Public | BindingFlags.GetProperty;
#endif

        #region Dead Code
#if DEAD_CODE
        internal const BindingFlags PrivateStaticSetPropertyBindingFlags = BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.SetProperty;
#endif
        #endregion

        internal const BindingFlags PrivateStaticMethodBindingFlags = BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.InvokeMethod;

        internal const BindingFlags PrivateInstanceGetFieldBindingFlags = BindingFlags.Instance |
            BindingFlags.NonPublic | BindingFlags.GetField;

#if TEST
        internal const BindingFlags PublicInstanceGetFieldBindingFlags = BindingFlags.Instance |
            BindingFlags.Public | BindingFlags.GetField;
#endif

#if WINFORMS
        #region Dead Code
#if DEAD_CODE
        internal const BindingFlags PrivateInstanceGetPropertyBindingFlags = BindingFlags.Instance |
            BindingFlags.NonPublic | BindingFlags.GetProperty;
#endif
        #endregion
#endif

#if TEST
        internal const BindingFlags PublicInstanceGetPropertyBindingFlags = BindingFlags.Instance |
            BindingFlags.Public | BindingFlags.GetProperty;
#endif

#if NATIVE && WINDOWS
        internal const BindingFlags PrivateInstanceMethodBindingFlags = BindingFlags.Instance |
            BindingFlags.NonPublic | BindingFlags.InvokeMethod;
#endif

        ///////////////////////////////////////////////////////////////////////////////////////////////

        /* IMMUTABLE */
        private static readonly IClientData ObjectValueData = ClientData.Empty;

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static readonly Type ComObjectType = Type.GetType("System.__ComObject");

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static readonly Type RuntimeType = CommonOps.Runtime.IsMono() ?
            Type.GetType("System.MonoType") : Type.GetType("System.RuntimeType");

        ///////////////////////////////////////////////////////////////////////////////////////////////

        internal static readonly string IdentityMemberName = "Identity";
        internal static readonly string TypeIdentityMemberName = "TypeIdentity";

        internal static readonly MemberInfo[] IdentityMemberInfo = typeof(MarshalOps).GetMember(
            IdentityMemberName, PublicStaticMethodBindingFlags);

        internal static readonly MemberInfo[] TypeIdentityMemberInfo = typeof(MarshalOps).GetMember(
            TypeIdentityMemberName, PublicStaticMethodBindingFlags);

        ///////////////////////////////////////////////////////////////////////////////////////////////

#if NATIVE
        ///////////////////////////////////////////////////////////////////////////////////////////////
        // Required Native APIs used via P/Invoke
        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Unsafe Native Methods Class
        [SuppressUnmanagedCodeSecurity()]
        [ObjectId("90b718a7-90f2-4fba-ba0d-caaf2c25b5ad")]
        internal static class UnsafeNativeMethods
        {
            [ComImport()]
            [Guid("b196b283-bab4-101a-b69c-00aa00341d07")]
            [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
            [ObjectId("031c7925-e195-4c67-b5fd-b60c8bbc757d")]
            public interface IProvideClassInfo
            {
                [return: MarshalAs(UnmanagedType.Interface)]
                ITypeInfo GetClassInfo();
            }

            ///////////////////////////////////////////////////////////////////////////////////////////

            [ComImport()]
            [Guid("00020400-0000-0000-c000-000000000046")]
            [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
            [ObjectId("2adc2a0c-f981-4800-8a1f-a34731420a9f")]
            public interface IDispatch
            {
                uint GetTypeInfoCount();

                [return: MarshalAs(UnmanagedType.Interface)]
                ITypeInfo GetTypeInfo(
                    [In()]
                    uint typeInfoId,
                    [In(), MarshalAs(UnmanagedType.U4)]
                    int localeId
                    );

                [PreserveSig()]
                int GetIDsOfNames(
                    [In()]
                    ref Guid iid,
                    [In(), MarshalAs(UnmanagedType.LPArray)]
                    string[] names,
                    [In()]
                    uint count,
                    [In(), MarshalAs(UnmanagedType.U4)]
                    int localeId,
                    [Out(), MarshalAs(UnmanagedType.LPArray)]
                    int[] dispatchIds
                    );

                [PreserveSig()]
                int Invoke(
                    [In()]
                    int dispatchId,
                    [In()]
                    ref Guid iid,
                    [In(), MarshalAs(UnmanagedType.U4)]
                    int localeId,
                    [In()]
                    uint flags,
                    [In(), Out()]
                    ref DISPPARAMS dispParams,
                    [Out()]
                    out object result,
                    [In(), Out()]
                    ref EXCEPINFO excepInfo,
                    [Out()]
                    out uint argumentError
                    );
            }
        }
        #endregion
#endif

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Dead Code
#if DEAD_CODE
        private static bool IsObjectHandle(
            Result result
            )
        {
            return (result != null) &&
                Object.ReferenceEquals(result.ValueData, ObjectValueData);
        }
#endif
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsSystemComObjectType(
            Type type
            )
        {
            return ((type != null) && Object.ReferenceEquals(type, ComObjectType));
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsNamespaceQualifiedTypeName(
            string typeName
            )
        {
            //
            // NOTE: If the type name is null or does not contain any periods,
            //       it must be a simple type name.
            //
            if ((typeName == null) ||
                typeName.IndexOf(Type.Delimiter) == Index.Invalid)
            {
                return false;
            }

            //
            // BUGFIX: This method is only used within the context of searching
            //         for a fully qualified type name and returning false from
            //         this method simply allows the search to include imported
            //         namespaces; therefore, we err on the side of caution and
            //         return true only if the type name could not possibly be
            //         a simple generic type name that happens to contain some
            //         generic type parameters that may be fully qualified type
            //         names.
            //
            int index = typeName.IndexOf(ParameterDelimiter);

            if (index != Index.Invalid)
                return typeName.Substring(0, index).IndexOf(Type.Delimiter) != Index.Invalid;

            return true;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsAssemblyQualifiedTypeName(
            string typeName
            )
        {
            //
            // BUGFIX: Must take into account generic types with more than one
            //         generic type parameter (i.e. that will contain commas).
            //
            if (!String.IsNullOrEmpty(typeName))
            {
                //
                // NOTE: Simply try to find a comma that is NOT within square
                //       brackets.  If we find one, return true.
                //
                int count = 0;

                for (int index = 0; index < typeName.Length; index++)
                {
                    char character = typeName[index];

                    switch (character)
                    {
                        case Characters.Comma:
                            {
                                if (count == 0)
                                    return true;
                                else
                                    break;
                            }
                        case Characters.OpenBracket:
                            {
                                count++;
                                break;
                            }
                        case Characters.CloseBracket:
                            {
                                count--;
                                break;
                            }
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsOneDimensionalArrayType(
            Type type
            )
        {
            Type elementType = null;

            return IsOneDimensionalArrayType(type, ref elementType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsOneDimensionalArrayType(
            Type type,
            ref Type elementType
            )
        {
            int rank = 0;

            if (IsArrayType(type, ref rank, ref elementType) &&
                (rank == 1) && (elementType != null))
            {
                return true;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetVariableValueAsString(
            Interpreter interpreter,
            object value,
            bool ignoreAlias,
            ref Result result
            )
        {
            ObjectFlags objectFlags = ObjectOps.GetDefaultObjectFlags();

            if (ignoreAlias)
                objectFlags |= ObjectFlags.IgnoreAlias;

            return FixupReturnValue(
                interpreter, null, objectFlags,
                ObjectOps.GetDefaultObjectOptionType(), null,
                value, false, false, false, ref result);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode GetArrayIndexes(
            CultureInfo cultureInfo,
            string index,
            ref int[] indexes
            )
        {
            Result error = null;

            return GetArrayIndexes(
                cultureInfo, index, ref indexes, ref error);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetArrayIndexes(
            CultureInfo cultureInfo,
            string index,
            ref int[] indexes,
            ref Result error
            )
        {
            if (index == null)
            {
                error = "invalid index";
                return ReturnCode.Error;
            }

            string[] subIndexes = index.Split(Characters.Comma);

            if (subIndexes == null)
            {
                error = String.Format(
                    "could not parse array index \"{0}\"",
                    index);

                return ReturnCode.Error;
            }

            int rank = subIndexes.Length;

            if (rank <= 0)
            {
                error = String.Format(
                    "invalid array rank {0}",
                    rank);

                return ReturnCode.Error;
            }

            int[] localIndexes = new int[rank];

            for (int rankIndex = 0; rankIndex < rank; rankIndex++)
            {
                if (Value.GetInteger2(
                        subIndexes[rankIndex], ValueFlags.AnyInteger,
                        cultureInfo, ref localIndexes[rankIndex],
                        ref error) != ReturnCode.Ok)
                {
                    return ReturnCode.Error;
                }
            }

            indexes = localIndexes;
            return ReturnCode.Ok;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode GetArrayElementValue(
            IBinder binder,
            CultureInfo cultureInfo,
            Array array,
            int[] indexes,
            ref object value
            )
        {
            Result error = null;

            return GetArrayElementValue(
                binder, cultureInfo, array, indexes, ref value, ref error);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetArrayElementValue(
            IBinder binder,
            CultureInfo cultureInfo,
            Array array,
            int[] indexes,
            ref object value,
            ref Result error
            )
        {
            if (array == null)
            {
                error = "invalid managed array";
                return ReturnCode.Error;
            }

            try
            {
                value = array.GetValue(indexes);
                return ReturnCode.Ok;
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode SetArrayElementValue(
            IBinder binder,
            CultureInfo cultureInfo,
            Array array,
            int[] indexes,
            object value,
            ref Result error
            )
        {
            if (array == null)
            {
                error = "invalid managed array";
                return ReturnCode.Error;
            }

            Type type = array.GetType();

            if (type == null)
            {
                error = "managed array an invalid type";
                return ReturnCode.Error;
            }

            Type elementType = null;

            if (!IsArrayType(type, ref elementType))
            {
                error = String.Format(
                    "unknown element type for managed array type {0}",
                    FormatOps.TypeName(type));

                return ReturnCode.Error;
            }

            if (binder == null)
            {
                error = "invalid binder";
                return ReturnCode.Error;
            }

            Type valueType = (value != null) ?
                value.GetType() : typeof(object);

            object localValue = null;
            bool changeType = true;

            if (IsSameType(valueType, elementType))
            {
                localValue = value;
                changeType = false;
            }
            else if (IsSimpleTypeForToString(binder, valueType))
            {
                Result result = null;

                if (ConvertValueToString(binder as ScriptBinder,
                        cultureInfo, valueType, null, value,
                        false, ref result) == ReturnCode.Ok)
                {
                    localValue = StringOps.GetStringFromObject(result);
                }
                else
                {
                    return ReturnCode.Error;
                }
            }
            else
            {
                localValue = StringOps.GetStringFromObject(value);
            }

            try
            {
                array.SetValue(changeType ?
                    binder.ChangeType(
                        localValue, elementType, cultureInfo) :
                    localValue, indexes);

                return ReturnCode.Ok;
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetArrayElementKeys(
            Interpreter interpreter,
            Array array,
            MatchMode mode,
            string pattern,
            bool noCase,
            ref StringList keys,
            ref Result error
            )
        {
            Result result = null;

            if (GetArrayElementKeysAndOrValues(
                    interpreter, array, true, false, mode, pattern,
                    null, noCase, ref result) == ReturnCode.Ok)
            {
                if (result != null)
                {
                    StringList list = result.Value as StringList;

                    if (list != null)
                    {
                        keys = list;
                        return ReturnCode.Ok;
                    }
                    else
                    {
                        error = String.Format(
                            "no {0} within result for {1} conversion",
                            FormatOps.TypeName(typeof(StringList)),
                            FormatOps.TypeName(typeof(Array)));
                    }
                }
                else
                {
                    error = String.Format(
                        "no result for {0} to {1} conversion",
                        FormatOps.TypeName(typeof(Array)),
                        FormatOps.TypeName(typeof(StringList)));
                }
            }
            else
            {
                error = result;
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetArrayElementValues(
            Interpreter interpreter,
            Array array,
            MatchMode mode,
            string pattern,
            bool noCase,
            ref StringList values,
            ref Result error
            )
        {
            Result result = null;

            if (GetArrayElementKeysAndOrValues(
                    interpreter, array, false, true, mode, null,
                    pattern, noCase, ref result) == ReturnCode.Ok)
            {
                if (result != null)
                {
                    StringList list = result.Value as StringList;

                    if (list != null)
                    {
                        values = list;
                        return ReturnCode.Ok;
                    }
                    else
                    {
                        error = String.Format(
                            "no {0} within result for {1} conversion",
                            FormatOps.TypeName(typeof(StringList)),
                            FormatOps.TypeName(typeof(Array)));
                    }
                }
                else
                {
                    error = String.Format(
                        "no result for {0} to {1} conversion",
                        FormatOps.TypeName(typeof(Array)),
                        FormatOps.TypeName(typeof(StringList)));
                }
            }
            else
            {
                error = result;
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetArrayElementKeysAndValues(
            Interpreter interpreter,
            Array array,
            MatchMode mode,
            string keyPattern,
            string valuePattern,
            bool noCase,
            ref StringDictionary keysAndValues,
            ref Result error
            )
        {
            Result result = null;

            if (GetArrayElementKeysAndOrValues(
                    interpreter, array, true, true, mode, keyPattern,
                    valuePattern, noCase, ref result) == ReturnCode.Ok)
            {
                if (result != null)
                {
                    StringDictionary dictionary =
                        result.Value as StringDictionary;

                    if (dictionary != null)
                    {
                        keysAndValues = dictionary;
                        return ReturnCode.Ok;
                    }
                    else
                    {
                        error = String.Format(
                            "no {0} within result for {1} conversion",
                            FormatOps.TypeName(typeof(StringDictionary)),
                            FormatOps.TypeName(typeof(Array)));
                    }
                }
                else
                {
                    error = String.Format(
                        "no result for {0} to {1} conversion",
                        FormatOps.TypeName(typeof(Array)),
                        FormatOps.TypeName(typeof(StringDictionary)));
                }
            }
            else
            {
                error = result;
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode GetArrayElementKeysAndOrValues(
            Interpreter interpreter,
            Array array,
            bool keys,
            bool values,
            MatchMode mode,
            string keyPattern,
            string valuePattern,
            bool noCase,
            ref Result result
            )
        {
            if (array == null)
            {
                result = String.Format(
                    "invalid {0} variable",
                    FormatOps.TypeName(typeof(Array)));

                return ReturnCode.Error;
            }

            if (keys || values)
            {
                StringDictionary dictionary = (keys && values) ?
                    new StringDictionary() : null;

                StringList list = (dictionary == null) ?
                    new StringList() : null;

                //
                // NOTE: Obtain all the bounds for the array.
                //
                int rank = 0;
                int[] lowerBounds = null;
                int[] lengths = null;
                int[] indexes = null;

                ArrayOps.GetBounds(
                    array, ref rank, ref lowerBounds, ref lengths,
                    ref indexes);

                //
                // NOTE: Iterate over the entire array, capturing the names
                //       (i.e. index strings) and/or values as we go.
                //
                int length = array.Length;

                for (int unused = 0; unused < length; unused++)
                {
                    string index = GenericOps<int>.ListToString(
                        indexes, 0, Index.Invalid, ToStringFlags.None,
                        Characters.Comma.ToString(), null, false);

                    string value = StringOps.GetStringFromObject(
                        array.GetValue(indexes));

                    if (((keyPattern == null) ||
                        StringOps.Match(null, mode, index, keyPattern, noCase)) &&
                        ((valuePattern == null) ||
                        StringOps.Match(null, mode, value, valuePattern, noCase)))
                    {
                        if (dictionary != null)
                        {
                            dictionary.Add(index, value);
                        }
                        else if (list != null)
                        {
                            if (keys)
                                list.Add(index);

                            if (values)
                                list.Add(value);
                        }
                    }

                    ArrayOps.IncrementIndexes(
                        rank, lowerBounds, lengths, indexes);
                }

                if (dictionary != null)
                    result = dictionary;
                else
                    result = list;
            }
            else
            {
                result = String.Empty;
            }

            return ReturnCode.Ok;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool DoesArrayElementExist(
            IBinder binder,
            CultureInfo cultureInfo,
            IVariable variable,
            string index
            )
        {
            Array array = EntityOps.GetSystemArray(variable);

            if (array == null)
                return false;

            int[] indexes = null;

            if (GetArrayIndexes(
                    cultureInfo, index, ref indexes) != ReturnCode.Ok)
            {
                return false;
            }

            object value = null; /* NOT USED */

            if (GetArrayElementValue(
                    binder, cultureInfo, array, indexes,
                    ref value) != ReturnCode.Ok)
            {
                return false;
            }

            return true;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool HasElementType(
            Type type,
            bool isArray,
            ref Type elementType
            )
        {
            if ((type != null) && type.HasElementType)
            {
                elementType = type.GetElementType();

                return (type.IsArray == isArray);
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsObjectType(
            Type type
            )
        {
            Type objectType = typeof(object);

            return (type == objectType) ||
                (type == objectType.MakeByRefType());
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsArrayValue(
            object value,
            ref Array array
            )
        {
            if (value == null)
                return false;

            array = value as Array;
            return (array != null);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsArrayType(
            IVariable variable,  /* in */
            ref int rank,        /* in, out */
            ref Type elementType /* in, out */
            )
        {
            if (variable == null)
                return false;

            ElementDictionary arrayValue = null;

            if (!EntityOps.IsArray(variable, ref arrayValue))
                return false;

            //
            // NOTE: An empty array has a rank of one, not zero.
            //
            int maximumRank = 1;

            //
            // NOTE: This loop is O(N) in the pathological case where
            //       all but the last array index are an empty string.
            //
            foreach (KeyValuePair<string, object> pair in arrayValue)
            {
                string index = pair.Key;

                if (String.IsNullOrEmpty(index))
                    continue; /* NOTE: Skip invalid array index. */

                string[] subIndexes = index.Split(Characters.Comma);

                if (subIndexes == null)
                    return false;

                int subIndexesLength = subIndexes.Length;

                if (subIndexesLength > maximumRank)
                    maximumRank = subIndexesLength;

                break;
            }

            if ((rank == 0) || (maximumRank > rank))
                rank = maximumRank;

            if (elementType == null)
                elementType = typeof(Variant);

            return true;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsArrayType(
            Type type
            )
        {
            Type elementType = null;

            return IsArrayType(type, ref elementType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsArrayType(
            Type type,
            ref Type elementType
            )
        {
            int rank = 0;

            return IsArrayType(type, ref rank, ref elementType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsArrayType(
            Type type,
            ref int rank,
            ref Type elementType
            )
        {
            if (type != null)
            {
                if (type.IsArray)
                {
                    rank = type.GetArrayRank();
                    elementType = type.GetElementType();

                    return true;
                }
                else if (type.IsByRef)
                {
                    Type byRefElementType = type.GetElementType();

                    if ((byRefElementType != null) && byRefElementType.IsArray)
                    {
                        rank = byRefElementType.GetArrayRank();
                        elementType = byRefElementType.GetElementType();

                        return true;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string JoinTypeName(
            string[] parts,
            int lastIndex
            )
        {
            if (parts == null)
                return null;

            if ((lastIndex < 0) || (lastIndex >= parts.Length))
                return null;

            return String.Join(
                Type.Delimiter.ToString(), parts, 0, lastIndex + 1);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string[] SplitTypeName(
            string typeName
            )
        {
            if (String.IsNullOrEmpty(typeName))
                return null;

            string[] typeParts = typeName.Split(Characters.GraveAccent);

            if ((typeParts == null) || (typeParts.Length == 0))
                return new string[] { typeName };

            string[] baseTypeParts = typeParts[0].Split(Type.Delimiter);

            if ((baseTypeParts == null) || (baseTypeParts.Length == 0))
                return new string[] { typeName };

            return baseTypeParts;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static VariableFlags GetArrayVariableFlags(
            bool input
            )
        {
            return VariableFlags.NoElement | VariableFlags.NoLinkIndex |
                (input ? VariableFlags.Defined : VariableFlags.None) |
                VariableFlags.NonVirtual;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static Type GetArrayElementType(
            Type elementType
            )
        {
            return (elementType == typeof(Variant)) ?
                typeof(object) : elementType;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsScalarWithNullOrEmptyValue(
            Interpreter interpreter,
            IVariable variable
            )
        {
            //
            // NOTE: Neither a null variable nor an array value qualify for
            //       this checking.
            //
            if ((variable == null) || EntityOps.IsArray2(variable))
                return false;

            object value = variable.Value; /* SCALAR */

            if (value == null)
                return true; /* NOTE: Physical null value. */

            if (!(value is string))
                return false;

            string stringValue = (string)value;

            if (stringValue.Length == 0)
                return true; /* NOTE: Physical empty value. */

            //
            // NOTE: Next, see if the variable actually represents an opaque
            //       object handle, where the object value itself is null or
            //       an empty string.
            //
            IObject @object = null;

            if ((interpreter != null) && (interpreter.GetObject(
                    stringValue, LookupFlags.MarshalNoVerbose,
                    ref @object) == ReturnCode.Ok))
            {
                //
                // NOTE: Technically, the value of "@object" should never
                //       be null at this point, given the lookup flags.
                //
                if (@object == null)
                    return true;

                if (@object.Value == null)
                    return true;

                if (@object.Value is string)
                {
                    /* REUSED */
                    stringValue = (string)@object.Value;

                    if (stringValue.Length == 0)
                        return true;
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsArray(
            Interpreter interpreter,     /* in */
            VariableFlags variableFlags, /* in */
            string name,                 /* in */
            ref IVariable variable       /* in, out */
            )
        {
            if (interpreter == null)
                return false;

            if ((variable == null) && interpreter.DoesVariableExist(
                    variableFlags, name, ref variable) != ReturnCode.Ok)
            {
                return false;
            }

            return EntityOps.IsArray2(variable);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSameType(
            Type type1,
            Type type2
            )
        {
            return IsValueType(type1) || IsValueType(type2) ?
                IsSameValueType(type1, type2) :
                IsSameReferenceType(type1, type2);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsSameValueType(
            Type type1,
            Type type2
            )
        {
            return IsSameValueType(type1, type2, true);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSameValueType(
            Type type1,
            Type type2,
            bool output
            )
        {
            return IsSameValueType(type1, type2, true, output);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSameValueType(
            Type type1,
            Type type2,
            bool nullable,
            bool output
            )
        {
            Type valueType = null;

            if (type1 == type2)
                return true;
            else if (nullable && IsNullableType(type1, output, ref valueType) && (type2 == valueType))
                return true;
            else if (nullable && IsNullableType(type2, output, ref valueType) && (type1 == valueType))
                return true;
            else if (output && !type1.IsByRef && (type1.MakeByRefType() == type2))
                return true;
            else if (output && !type2.IsByRef && (type2.MakeByRefType() == type1))
                return true;
            else
                return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsSameReferenceType(
            Type type1,
            Type type2
            )
        {
            return IsSameReferenceType(type1, type2, true);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSameReferenceType(
            Type type1,
            Type type2,
            bool output
            )
        {
            if ((type1 == type2) || type2.IsAssignableFrom(type1))
                return true;
            else if (output && !type1.IsByRef && type2.IsAssignableFrom(type1.MakeByRefType()))
                return true;
            else
                return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsValueType(
            Type type
            )
        {
            return IsValueType(type, false);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsValueType(
            Type type,
            bool output
            )
        {
            Type elementType = null;

            return IsValueType(type, output, ref elementType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsValueType(
            Type type,
            bool output,
            ref Type elementType
            )
        {
            if (type != null)
            {
                if (type.IsValueType)
                {
                    elementType = type;

                    return true;
                }
                else if (output && type.IsByRef)
                {
                    Type byRefElementType = type.GetElementType();

                    if ((byRefElementType != null) && byRefElementType.IsValueType)
                    {
                        elementType = byRefElementType;

                        return true;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsNullableType(
            Type type
            )
        {
            return IsNullableType(type, false);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsNullableType(
            Type type,
            bool output
            )
        {
            Type valueType = null;

            return IsNullableType(type, output, ref valueType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsNullableType(
            Type type,
            bool output,
            ref Type valueType
            )
        {
            if (type != null)
            {
                if (type.IsGenericType &&
                    (type.GetGenericTypeDefinition() == typeof(Nullable<>)))
                {
                    Type[] genericArguments = type.GetGenericArguments();

                    if ((genericArguments != null) && (genericArguments.Length > 0))
                        valueType = genericArguments[0];

                    return true;
                }
                else if (output && type.IsByRef)
                {
                    Type byRefElementType = type.GetElementType();

                    if ((byRefElementType != null) &&
                        byRefElementType.IsGenericType &&
                        (byRefElementType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                    {
                        Type[] genericArguments = byRefElementType.GetGenericArguments();

                        if ((genericArguments != null) && (genericArguments.Length > 0))
                            valueType = genericArguments[0];

                        return true;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string WrapHandle(
            Interpreter interpreter,
            object value
            )
        {
            //
            // HACK: Currently, it is impossible to pass a string that
            //       happens to represent an existing opaque object
            //       handle to any managed method via [object invoke],
            //       et al.  This is because there must be internal
            //       calls (inside the binder and method overload
            //       resolution engine) that automatically convert any
            //       such string to the underlying raw object value for
            //       [object invoke] to be truly useful.  This method
            //       is an extremely nasty hack that works around this
            //       issue.
            //
            if (interpreter != null)
            {
                string name = null;

                if (interpreter.GetObject(
                        value, LookupFlags.MarshalNoVerbose,
                        ref name) == ReturnCode.Ok)
                {
                    Result result = null;

                    if (FixupReturnValue(interpreter,
                            interpreter.Binder, interpreter.CultureInfo, null,
                            ObjectFlags.None, null, ObjectOptionType.None, null,
                            null, name, true, true, false, false, false,
                            ref result) == ReturnCode.Ok)
                    {
                        return result;
                    }
                }
                else if (value is string)
                {
                    return (string)value;
                }
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static object Identity(
            object arg
            )
        {
            //
            // NOTE: Used by [object invoke -identity] to allow the
            //       FixupReturnValue handling to be used on any
            //       object.
            //
            return arg;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static Type TypeIdentity(
            Type arg
            )
        {
            //
            // NOTE: Used by [object invoke -typeidentity] to allow the
            //       FixupReturnValue handling to be used on any Type
            //       object.
            //
            return arg;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool MatchParameterTypes(
            TypeList parameterTypes,       /* in */
            ParameterInfo[] parameterInfo, /* in */
            bool strictCount,              /* in */
            bool strictType,               /* in */
            ref Result error               /* out */
            )
        {
            if (parameterTypes != null)
            {
                if (parameterInfo != null)
                {
                    int parameterLength = parameterInfo.Length;

                    if (!strictCount ||
                        (parameterLength == parameterTypes.Count))
                    {
                        int minimumLength = Math.Min(
                            parameterLength, parameterTypes.Count);

                        for (int parameterIndex = 0;
                                parameterIndex < minimumLength;
                                parameterIndex++)
                        {
                            Type parameterType = parameterTypes[parameterIndex];

                            if (parameterType == null)
                                continue;

                            Type parameterInfoType =
                                parameterInfo[parameterIndex].ParameterType;

                            string parameterInfoName =
                                parameterInfo[parameterIndex].Name;

                            if (strictType)
                            {
                                if (parameterType != parameterInfoType)
                                {
                                    error = String.Format(
                                        "parameter {0} type mismatch, type \"{1}\" is not " +
                                        "equal to type \"{2}\"", FormatOps.ArgumentName(
                                        parameterIndex, parameterInfoName), parameterInfoType,
                                        parameterType);

                                    return false;
                                }
                            }
                            else
                            {
                                if ((parameterInfoType == null) ||
                                    !parameterInfoType.IsAssignableFrom(parameterType))
                                {
                                    error = String.Format(
                                        "parameter {0} type mismatch, type \"{1}\" is not " +
                                        "assignable from type \"{2}\"", FormatOps.ArgumentName(
                                        parameterIndex, parameterInfoName), parameterInfoType,
                                        parameterType);

                                    return false;
                                }
                            }
                        }

                        return true;
                    }
                    else
                    {
                        error = String.Format(
                            "paramter count {0} is not equal to type count {1}",
                            parameterLength, parameterTypes.Count);
                    }
                }
                else
                {
                    error = "invalid parameter info";
                }
            }
            else
            {
                error = "invalid parameter types";
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

#if !MONO
        private static ReturnCode ResolveAssemblyClean(
            string name,
            ref Result result
            )
        {
            if (!String.IsNullOrEmpty(name))
            {
                int retry = 0;

                //
                // NOTE: If we cannot resolve the assembly using the clean method,
                //       we will fallback to attempting to load it.
                //
            fallback:
                if (retry == 0)
                {
                    MethodInfo methodInfo = null;

                    if (CommonOps.Runtime.IsFramework20()) // NOTE: Assumes !IsMono.
                    {
                        //
                        // NOTE: The .NET Framework 2.0 and 3.5 define the method
                        //       we need on the Assembly type; therefore, we will
                        //       try there (and only there).
                        //
                        methodInfo = typeof(Assembly).GetMethod(
                            EnumerateCacheMethodName, PrivateStaticMethodBindingFlags);
                    }
                    else if (CommonOps.Runtime.IsFramework40()) // NOTE: Assumes !IsMono.
                    {
                        //
                        // NOTE: The .NET Framework 4.0 defines the method we need
                        //       on the RuntimeAssembly type; therefore, we will
                        //       try there (and only there).
                        //
                        Type type = Type.GetType(RuntimeAssemblyTypeName);

                        if (type != null)
                            methodInfo = type.GetMethod(
                                EnumerateCacheMethodName, PrivateStaticMethodBindingFlags);
                    }

                    if (methodInfo != null)
                    {
                        try
                        {
                            //
                            // NOTE: Create an assembly name object using the string
                            //       provided by the script (verbatim).
                            //
                            AssemblyName oldAssemblyName = new AssemblyName(name);

                            //
                            // HACK: This method is private in the .NET Framework and we
                            //       need to use it; therefore, we use reflection to
                            //       invoke it.  This method requires the "UnmanagedCode"
                            //       permission.
                            //
                            AssemblyName newAssemblyName = methodInfo.Invoke(
                                null, new object[] { oldAssemblyName }) as AssemblyName;

                            //
                            // NOTE: Return non-zero if the assembly name was found in the
                            //       GAC.
                            //
                            if (newAssemblyName != null)
                            {
                                //
                                // NOTE: Return the newly resolved assembly name string.
                                //
                                result = newAssemblyName.ToString();

                                return ReturnCode.Ok;
                            }
                            else
                            {
                                result = String.Format(
                                    "could not resolve assembly based on partial name \"{0}\"",
                                    name);
                            }
                        }
                        catch (Exception e)
                        {
                            result = e;
                        }
                    }
                    else
                    {
                        //
                        // NOTE: We cannot use the preferred method to resolve the
                        //       assembly; fallback to attempting to actually load
                        //       it.
                        //
                        retry++;

                        goto fallback;
                    }
                }
                else
                {
                    //
                    // NOTE: *FALLBACK* In case the clean method does not work.
                    //
                    return ResolveAssemblyDefault(name, ref result);
                }
            }
            else
            {
                result = "invalid assembly name";
            }

            return ReturnCode.Error;
        }
#endif

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode ResolveAssemblyDefault(
            string name,
            ref Result result
            )
        {
            if (!String.IsNullOrEmpty(name))
            {
                try
                {
#if MONO_BUILD
#pragma warning disable 612
#endif
                    Assembly assembly = Assembly.LoadWithPartialName(name);
#if MONO_BUILD
#pragma warning restore 612
#endif

                    if (assembly != null)
                    {
                        //
                        // NOTE: Return the newly resolved assembly name string.
                        //
                        result = assembly.FullName;

                        return ReturnCode.Ok;
                    }
                    else
                    {
                        result = String.Format(
                            "could not resolve assembly based on partial name \"{0}\"",
                            name);
                    }
                }
                catch (Exception e)
                {
                    result = e;
                }
            }
            else
            {
                result = "invalid assembly name";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode ResolveAssembly(
            string name,
            ref Result result
            )
        {
#if !MONO
            if (!CommonOps.Runtime.IsMono())
                return ResolveAssemblyClean(name, ref result);
            else
#endif
                return ResolveAssemblyDefault(name, ref result);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static int SelectMethodIndex(
            Interpreter interpreter,  /* in */
            IBinder binder,           /* in */
            CultureInfo cultureInfo,  /* in */
            Type type,                /* in */
            MethodBase[] methods,     /* in */
            TypeList parameterTypes,  /* in */
            object[] args,            /* in */
            IntList methodIndexList,  /* in */
            ObjectArrayList argsList, /* in */
            MarshalFlags marshalFlags /* in */
            )
        {
            if ((methodIndexList == null) || (methodIndexList.Count < 1))
                return Index.Invalid;

            int defaultMethodIndex = methodIndexList[0];
            int methodIndex = defaultMethodIndex;
            IScriptBinder scriptBinder = binder as IScriptBinder;

            if (scriptBinder != null)
            {
                ReturnCode code;
                Result error = null;

                code = scriptBinder.SelectMethodIndex(
                    type, cultureInfo, parameterTypes, methods, args,
                    methodIndexList, argsList, ref methodIndex, ref error);

                if (code == ReturnCode.Ok)
                    return methodIndex;
                else if (code == ReturnCode.Break)
                    return Index.Invalid;
                else if (code == ReturnCode.Continue)
                    return defaultMethodIndex;

                if (FlagOps.HasFlags(marshalFlags, MarshalFlags.Verbose, true))
                    DebugOps.Complain(interpreter, code, error);
            }

            return methodIndex;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ArgumentInfo CreateArgumentInfo(
            int parameterIndex,
            Type parameterType,
            bool output
            )
        {
            //
            // TODO: All parameters are treated as either input -OR-
            //       input/output, which should be what conforms to
            //       COM.  Verify that this will work in all cases.
            //
            return CreateArgumentInfo(
                parameterIndex, parameterType, true, output);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ArgumentInfo CreateArgumentInfo(
            int parameterIndex,
            Type parameterType,
            bool input,
            bool output
            )
        {
            return ArgumentInfo.Create(parameterIndex, parameterType,
                GetParameterName(parameterIndex, parameterType),
                input, output);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode FixupArguments(
            Interpreter interpreter,   /* in */
            IBinder binder,            /* in */
            CultureInfo cultureInfo,   /* in */
            string objectName,         /* in */
            string methodName,         /* in */
            TypeList parameterTypes,   /* in */
            MarshalFlags marshalFlags, /* in */
            object[] args,             /* in, out */
            bool strict,               /* in */
            ref Result error           /* out */
            )
        {
            if (parameterTypes == null)
            {
                error = "invalid parameter type list";
                return ReturnCode.Error;
            }

            if (args == null)
            {
                error = "invalid argument array";
                return ReturnCode.Error;
            }

            int argumentLength = args.Length;
            int parameterCount = parameterTypes.Count;

            if (strict && (argumentLength != parameterCount))
            {
                error = String.Format(
                    "method {0} requires exactly {1} {2} and {3} {4} supplied",
                    FormatOps.MethodOverload(Index.Invalid, objectName, methodName),
                    parameterCount, (parameterCount != 1) ? "arguments" : "argument",
                    argumentLength, (argumentLength != 1) ? "were" : "was");

                return ReturnCode.Error;
            }

            object[] newArgs = new object[argumentLength];

            for (int argumentIndex = 0;
                    argumentIndex < argumentLength;
                    argumentIndex++)
            {
                object arg = args[argumentIndex];

                Type parameterType = (argumentIndex < parameterCount) ?
                    parameterTypes[argumentIndex] : null;

                if (parameterType != null)
                {
                    bool output = parameterType.IsByRef;

                    if (FixupArgument(
                            interpreter, binder, cultureInfo, parameterType,
                            CreateArgumentInfo(argumentIndex, parameterType,
                            output), marshalFlags, true, true, output, ref arg,
                            ref error) == ReturnCode.Ok)
                    {
                        newArgs[argumentIndex] = arg;
                    }
                    else
                    {
                        return ReturnCode.Error;
                    }
                }
                else
                {
                    newArgs[argumentIndex] = arg;
                }
            }

            //
            // NOTE: Success, commit changes to caller's original arguments.
            //
            Array.Copy(newArgs, args, argumentLength);
            return ReturnCode.Ok;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetByRefArgumentInfo(
            TypeList parameterTypes,               /* in */
            MarshalFlags marshalFlags,             /* in */
            ref ArgumentInfoList argumentInfoList, /* out */
            ref Result error                       /* out */
            )
        {
            if (parameterTypes == null)
            {
                error = "invalid parameter type list";
                return ReturnCode.Error;
            }

            for (int parameterIndex = 0;
                    parameterIndex < parameterTypes.Count;
                    parameterIndex++)
            {
                Type parameterType = parameterTypes[parameterIndex];

                if (parameterType == null)
                    continue;

                bool output = parameterType.IsByRef;

                if (output)
                {
                    if (argumentInfoList == null)
                        argumentInfoList = new ArgumentInfoList();

                    argumentInfoList.Add(CreateArgumentInfo(
                        parameterIndex, parameterType, output));
                }
            }

            return ReturnCode.Ok;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode FindMethodsAndFixupArguments( /* METHOD OVERLOAD RESOLUTION ENGINE */
            Interpreter interpreter,               /* in */
            IBinder binder,                        /* in */
            CultureInfo cultureInfo,               /* in */
            Type type,                             /* in */
            string objectName,                     /* in, NOT USED, RESERVED */
            string fullObjectName,                 /* in */
            string methodName,                     /* in */
            string fullMethodName,                 /* in */
            MemberTypes memberTypes,               /* in */
            BindingFlags bindingFlags,             /* in */
            MethodBase[] methods,                  /* in */
            TypeList parameterTypes,               /* in */
            object[] args,                         /* in */
            int limit,                             /* in */
            MarshalFlags marshalFlags,             /* in */
            ref IntList methodIndexList,           /* out */
            ref ObjectArrayList argsList,          /* out */
            ref ArgumentInfoList argumentInfoList, /* out */
            ref ResultList errors                  /* out */
            )
        {
            bool verbose = FlagOps.HasFlags(
                marshalFlags, MarshalFlags.Verbose, true);

            if (methods != null)
            {
                bool strictMatchCount = FlagOps.HasFlags(
                    marshalFlags, MarshalFlags.StrictMatchCount, true);

                bool strictMatchType = FlagOps.HasFlags(
                    marshalFlags, MarshalFlags.StrictMatchType, true);

                bool forceParameterType = FlagOps.HasFlags(
                    marshalFlags, MarshalFlags.ForceParameterType, true);

                //
                // NOTE: What method name, if any, did we match to?
                //
                string matchMethodName = null;

                //
                // NOTE: Process each method info and attempt to match it against
                //       the supplied arguments taking into account the number and
                //       coerced types of the arguments, the (in, out) attributes,
                //       and the params argument, if any.
                //
                for (int matchIndex = 0; matchIndex < methods.Length; matchIndex++)
                {
                    //
                    // NOTE: Get the method for the current method.
                    //
                    MethodBase method = methods[matchIndex];

                    if (method != null)
                    {
                        //
                        // NOTE: We need the name of the method to check.
                        //
                        string checkMethodName = methods[matchIndex].Name;

                        //
                        // NOTE: If the method name appears to be invalid, just skip it.
                        //
                        if (!String.IsNullOrEmpty(checkMethodName))
                        {
                            //
                            // NOTE: The method name must be matched first since we have
                            //       likely been passed all the methods for this type.
                            //
                            if (MatchMethodName(checkMethodName, methodName, memberTypes, bindingFlags))
                            {
                                //
                                // NOTE: Does the caller want to attempt to convert
                                //       arguments and filter on them?
                                //
                                if (args != null)
                                {
                                    //
                                    // NOTE: Set the name match index so that even if we ultimately
                                    //       do not find a matching method at least the error handling
                                    //       code knows that we got this far.
                                    //
                                    matchMethodName = checkMethodName;

                                    //
                                    // NOTE: We need the parameters of the method to check.
                                    //
                                    ParameterInfo[] checkParameterInfo = methods[matchIndex].GetParameters();

                                    //
                                    // NOTE: If the parameters appear to be invalid, just skip them.
                                    //
                                    if (checkParameterInfo != null)
                                    {
                                        int minimumCount = 0;
                                        int maximumCount = 0;
                                        Result error = null;

                                        if (GetParameterCounts(checkParameterInfo,
                                                ref minimumCount, ref maximumCount, ref error) == ReturnCode.Ok)
                                        {
                                            //
                                            // NOTE: Get the number of arguments supplied by the caller.
                                            //
                                            int argumentLength = args.Length;

                                            //
                                            // NOTE: Does the number of arguments supplied by the caller fit
                                            //       within the minimum and maximum number of arguments for
                                            //       this method?
                                            //
                                            if (((minimumCount == Count.Invalid) || (argumentLength >= minimumCount)) &&
                                                ((maximumCount == Count.Invalid) || (argumentLength <= maximumCount)))
                                            {
                                                //
                                                // NOTE: Check for any parameter type hints provided by the caller.
                                                //       If they were supplied then they must match up with the
                                                //       parameter types for this method.
                                                //
                                                if ((parameterTypes == null) || MatchParameterTypes(parameterTypes,
                                                        checkParameterInfo, strictMatchCount, strictMatchType,
                                                        ref error))
                                                {
                                                    //
                                                    // NOTE: Get the number of formal arguments for the method to
                                                    //       check.
                                                    //
                                                    int checkArgumentLength = checkParameterInfo.Length;

                                                    //
                                                    // NOTE: Create a temporary holding area for attempted argument
                                                    //       type conversions because in the case we do not fully
                                                    //       match the method we do not want to fiddle with the
                                                    //       caller's original arguments array.
                                                    //
                                                    object[] newArgs = new object[checkArgumentLength];

                                                    //
                                                    // NOTE: Start consuming the original arguments at the first
                                                    //       argument.
                                                    //
                                                    int argumentIndex = 0;

                                                    //
                                                    // NOTE: Reset the value of the argument being processed.
                                                    //
                                                    object matchArgumentValue = null;

                                                    //
                                                    // NOTE: Process each formal argument, checking the various
                                                    //       constraints imposed by it upon the supplied argument
                                                    //       value.
                                                    //
                                                    for (int parameterIndex = 0; parameterIndex < checkArgumentLength; parameterIndex++)
                                                    {
                                                        //
                                                        // NOTE: Check argument attributes to see if they are ref (in, out) or out (out).
                                                        //       Enforce argument being an existing script variable name for ref.
                                                        //       Enforce argument being a script variable name for out.
                                                        //
                                                        //       Also, arrays need to be handled in concert with (in, out).  Script arrays
                                                        //       will be used and the indexes will always be in the form (x) or (x,y,z)
                                                        //       where x is an integer index into a single or multi-dimensional array, and
                                                        //       y and z, etc are integer indexes of a multi-dimensional array.
                                                        //
                                                        ParameterInfo parameterInfo = checkParameterInfo[parameterIndex];

                                                        if (parameterInfo == null)
                                                            break; // BUGBUG: Better error message here.

                                                        Type parameterType;

                                                        if (forceParameterType)
                                                        {
                                                            if ((parameterTypes != null) &&
                                                                (parameterIndex < parameterTypes.Count))
                                                            {
                                                                parameterType = parameterTypes[parameterIndex];
                                                            }
                                                            else
                                                            {
                                                                parameterType = parameterInfo.ParameterType;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            parameterType = parameterInfo.ParameterType;
                                                        }

                                                        int parameterPosition = parameterInfo.Position;
                                                        string parameterName = parameterInfo.Name;

                                                        //
                                                        // NOTE: Check if this parameter is of a pointer type.
                                                        //
                                                        if (parameterType.IsPointer)
                                                        {
                                                            //
                                                            // NOTE: We do not allow and cannot process pointer types.
                                                            //
                                                            break; // BUGBUG: Better error message here.
                                                        }
                                                        else if (parameterInfo.IsDefined(typeof(ParamArrayAttribute), false))
                                                        {
                                                            //
                                                            // NOTE: Inside the loop, we need the type of the elements in the params array.
                                                            //
                                                            Type elementType = parameterType.GetElementType();

                                                            //
                                                            // NOTE: For simplicity, we will add each supplied argument to the params list
                                                            //       and convert the whole thing to an array at the end of the loop (when
                                                            //       we place the params array into the new arguments array at the current
                                                            //       argument index).
                                                            //
                                                            ObjectList @params = new ObjectList();

                                                            //
                                                            // NOTE: Save off the starting argument index because that is where we need to
                                                            //       place the params array.
                                                            //
                                                            int paramsIndex = argumentIndex; // save for below.

                                                            //
                                                            // NOTE: Use all the remaining arguments for this params array.  None of these
                                                            //       are allowed to be ByRef, therefore, we just need to perform type
                                                            //       coercion on the remaining argument strings (which will all end up being
                                                            //       of the same type).
                                                            //
                                                            for (; argumentIndex < argumentLength; argumentIndex++)
                                                            {
                                                                //
                                                                // NOTE: Start with their original argument value.
                                                                //
                                                                matchArgumentValue = args[argumentIndex];

                                                                if (FixupArgument(
                                                                        interpreter, binder, cultureInfo, elementType,
                                                                        ArgumentInfo.Create(argumentIndex, elementType,
                                                                            parameterName, true, false), MarshalFlags.None,
                                                                        true, true, false, ref matchArgumentValue,
                                                                        ref error) == ReturnCode.Ok)
                                                                {
                                                                    @params.Add(matchArgumentValue);
                                                                }
                                                                else
                                                                {
                                                                    if (errors == null)
                                                                        errors = new ResultList();

                                                                    if (verbose || (errors.Count == 0))
                                                                    {
                                                                        error = String.Format(
                                                                            "could not convert method {0} argument {1} with " +
                                                                            "value \"{2}\" to type \"{3}\": {4}",
                                                                            FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                            FormatOps.ArgumentName(parameterPosition, parameterName),
                                                                            matchArgumentValue, elementType.FullName, error);

                                                                        errors.Add(error);
                                                                    }
                                                                    break;
                                                                }
                                                            }

                                                            //
                                                            // NOTE: Did we successfully complete the argument processing
                                                            //       loop above?  If so, we need to resize (expand or truncate)
                                                            //       the new arguments array because there may have been 0
                                                            //       supplied arguments or we may have just collapsed N supplied
                                                            //       arguments into 1 params argument.
                                                            //
                                                            if (argumentIndex == argumentLength)
                                                            {
                                                                //
                                                                // NOTE: Make sure that the params array argument is the final
                                                                //       argument.  This includes forcing the outer for loop to
                                                                //       recognize that we have matched this method and are
                                                                //       totally done processing the supplied arguments by
                                                                //       reducing the supplied argument count and setting the
                                                                //       argument index to match.
                                                                //
                                                                argumentLength = paramsIndex + 1;
                                                                argumentIndex = argumentLength;

                                                                //
                                                                // NOTE: Physically resize the new arguments array to exactly
                                                                //       match the argument count; whether it is larger or
                                                                //       smaller does not matter.
                                                                //
                                                                Array.Resize(ref newArgs, argumentLength);

                                                                //
                                                                // NOTE: Store the params list into the corresponding new
                                                                //       argument slot.
                                                                //
                                                                newArgs[paramsIndex] = Array.CreateInstance(elementType,
                                                                    @params.Count);

                                                                Array.Copy(@params.ToArray(), (Array)newArgs[paramsIndex],
                                                                    @params.Count);
                                                            }

                                                            //
                                                            // NOTE: We just used up the all supplied arguments (or
                                                            //       we encountered an error that prevents us from
                                                            //       continuing to process this method as a match).
                                                            //
                                                            break;
                                                        }
                                                        else
                                                        {
                                                            //
                                                            // FIXME: This is not 100% correct.  The .NET Framework and C#
                                                            //        are not consistent when it comes to setting the
                                                            //        parameter input and output properties.  More work
                                                            //        needs to be done to properly figure out the semantics
                                                            //        used by C#.
                                                            //
                                                            bool output = parameterType.IsByRef || parameterInfo.IsOut;
                                                            bool input = parameterInfo.IsIn || !parameterInfo.IsOut;
                                                            bool optional = parameterInfo.IsOptional;

                                                            //
                                                            // NOTE: Set the value of this argument.
                                                            //
                                                            if (argumentIndex < argumentLength)
                                                            {
                                                                //
                                                                // NOTE: Use the supplied value.
                                                                //
                                                                matchArgumentValue = args[argumentIndex];
                                                            }
                                                            else if (optional)
                                                            {
                                                                //
                                                                // NOTE: Use the default value (optional argument).
                                                                //
                                                                matchArgumentValue = parameterInfo.DefaultValue;
                                                            }
                                                            else
                                                            {
                                                                //
                                                                // NOTE: Use null.  If we get to this point, that should mean
                                                                //       that non-optional arguments are present after optional
                                                                //       ones; otherwise, we would have enough of the supplied
                                                                //       arguments to satisfy the formal arguments.
                                                                //
                                                                matchArgumentValue = null;
                                                            }

                                                            //
                                                            // NOTE: Set the argument value to convert.
                                                            //
                                                            newArgs[argumentIndex] = matchArgumentValue;

                                                            //
                                                            // NOTE: Attempt to convert the argument to the expected type.
                                                            //
                                                            if (FixupArgument(
                                                                    interpreter, binder, cultureInfo, parameterType,
                                                                    ArgumentInfo.Create(argumentIndex, parameterType,
                                                                        parameterName, input, output), marshalFlags,
                                                                    true, input, output, ref newArgs[argumentIndex],
                                                                    ref error) == ReturnCode.Ok)
                                                            {
                                                                if (output)
                                                                {
                                                                    if (argumentInfoList == null)
                                                                        argumentInfoList = new ArgumentInfoList();

                                                                    //
                                                                    // NOTE: Original args, not newArgs, because we need the
                                                                    //       variable name, not the value.
                                                                    //
                                                                    argumentInfoList.Add(ArgumentInfo.Create(
                                                                        argumentIndex, parameterType,
                                                                        (string)matchArgumentValue, input, output));
                                                                }
                                                            }
                                                            else
                                                            {
                                                                //
                                                                // NOTE: We could not convert the supplied argument to
                                                                //       the required type for this method.
                                                                //
                                                                if (errors == null)
                                                                    errors = new ResultList();

                                                                if (verbose || (errors.Count == 0))
                                                                {
                                                                    error = String.Format(
                                                                        "could not convert method {0} argument {1} with " +
                                                                        "value \"{2}\" to type \"{3}\": {4}",
                                                                        FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                        FormatOps.ArgumentName(parameterPosition, parameterName),
                                                                        newArgs[argumentIndex], parameterType.FullName, error);

                                                                    errors.Add(error);
                                                                }
                                                                break;
                                                            }

                                                            argumentIndex++;
                                                        }
                                                    }

                                                    //
                                                    // NOTE: Did we match up all arguments with their formal parameters?
                                                    //
                                                    if (argumentIndex == checkArgumentLength)
                                                    {
                                                        if (methodIndexList == null)
                                                            methodIndexList = new IntList();

                                                        methodIndexList.Add(matchIndex);

                                                        if (argsList == null)
                                                            argsList = new ObjectArrayList();

                                                        argsList.Add(newArgs);

                                                        if ((limit > 0) && (methodIndexList.Count >= limit))
                                                            return ReturnCode.Ok;
                                                    }
                                                }
                                                else
                                                {
                                                    if (errors == null)
                                                        errors = new ResultList();

                                                    if (verbose || (errors.Count == 0))
                                                    {
                                                        error = String.Format(
                                                            "cannot match parameter types with method {0}: {1}",
                                                            FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                            error);

                                                        errors.Add(error);
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                if (errors == null)
                                                    errors = new ResultList();

                                                if (verbose || (errors.Count == 0))
                                                {
                                                    //
                                                    // NOTE: Create an appropriate error message based on how
                                                    //       exactly the parameter do not match.
                                                    //
                                                    if (minimumCount > 0)
                                                    {
                                                        if (maximumCount != Count.Invalid)
                                                        {
                                                            if (maximumCount != minimumCount)
                                                            {
                                                                error = String.Format(
                                                                    "method {0} requires between {1} and {2} " +
                                                                    "arguments and {3} {4} supplied",
                                                                    FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                    minimumCount, maximumCount, argumentLength,
                                                                    (argumentLength != 1) ? "were" : "was");
                                                            }
                                                            else
                                                            {
                                                                error = String.Format(
                                                                    "method {0} requires exactly {1} " +
                                                                    "{2} and {3} {4} supplied",
                                                                    FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                    maximumCount, (maximumCount != 1) ? "arguments" : "argument",
                                                                    argumentLength, (argumentLength != 1) ? "were" : "was");
                                                            }
                                                        }
                                                        else
                                                        {
                                                            error = String.Format(
                                                                "method {0} requires at least {1} " +
                                                                "{2} and {3} {4} supplied",
                                                                FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                minimumCount, (minimumCount != 1) ? "arguments" : "argument",
                                                                argumentLength, (argumentLength != 1) ? "were" : "was");
                                                        }
                                                    }
                                                    else
                                                    {
                                                        if (maximumCount != Count.Invalid)
                                                        {
                                                            error = String.Format(
                                                                "method {0} requires at most {1} " +
                                                                "{2} and {3} {4} supplied",
                                                                FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                maximumCount, (maximumCount != 1) ? "arguments" : "argument",
                                                                argumentLength, (argumentLength != 1) ? "were" : "was");
                                                        }
                                                        else
                                                        {
                                                            //
                                                            // NOTE: We should not really get here.  That
                                                            //       being said, we still try to provide
                                                            //       a meaningful error message.
                                                            //
                                                            error = String.Format(
                                                                "method {0} does not take {1} {2}",
                                                                FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                                argumentLength, (argumentLength != 1) ? "arguments" : "argument");
                                                        }
                                                    }

                                                    errors.Add(error);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (errors == null)
                                                errors = new ResultList();

                                            if (verbose || (errors.Count == 0))
                                            {
                                                error = String.Format(
                                                    "cannot get parameter counts for method {0}: {1}",
                                                    FormatOps.MethodOverload(matchIndex, fullObjectName, fullMethodName),
                                                    error);

                                                errors.Add(error);
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    if (methodIndexList == null)
                                        methodIndexList = new IntList();

                                    methodIndexList.Add(matchIndex);

                                    if (argsList == null)
                                        argsList = new ObjectArrayList();

                                    argsList.Add(args);

                                    if ((limit > 0) && (methodIndexList.Count >= limit))
                                        return ReturnCode.Ok;
                                }
                            }
                        }
                    }
                }

                //
                // NOTE: Did we find any matches?  If one or more matching
                //       methods were found, this is a success.  We can get
                //       here if we did not exceed the method limit supplied
                //       by the caller (or if there is no limit).
                //
                if ((methodIndexList != null) && (methodIndexList.Count > 0))
                    return ReturnCode.Ok;

                //
                // NOTE: Did we even find a method with a matching name?
                //       This logic is designed purely to produce a slightly
                //       better error message.
                //
                if (matchMethodName == null)
                {
                    if (errors == null)
                        errors = new ResultList();

                    if (verbose || (errors.Count == 0))
                    {
                        errors.Add(String.Format(
                            "method {0} not found",
                            FormatOps.MethodOverload(Index.Invalid, fullObjectName, fullMethodName)));
                    }
                }
            }
            else
            {
                if (errors == null)
                    errors = new ResultList();

                if (verbose || (errors.Count == 0))
                    errors.Add("invalid method array");
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Dead Code
#if DEAD_CODE
        [Obsolete()]
        private static ReturnCode OldFindMethodAndFixupArguments( /* NOT USED */
            Interpreter interpreter,               /* in */
            IBinder binder,                        /* in */
            CultureInfo cultureInfo,               /* in */
            Type type,                             /* in */
            string objectName,                     /* in */
            string fullObjectName,                 /* in */
            string methodName,                     /* in */
            string fullMethodName,                 /* in */
            MemberTypes memberTypes,               /* in */
            BindingFlags bindingFlags,             /* in */
            MethodBase[] methods,                  /* in */
            TypeList parameterTypes,               /* in */
            MarshalFlags marshalFlags,             /* in */
            ref object[] args,                     /* in, out */
            ref int methodIndex,                   /* out */
            ref ArgumentInfoList argumentInfoList, /* out */
            ref Result error                       /* out */
            )
        {
            IntList methodIndexList = null;
            ObjectArrayList argsList = null;
            ResultList errors = null;

            if (FindMethodsAndFixupArguments(
                    interpreter, binder, cultureInfo, type, objectName,
                    fullObjectName, methodName, fullMethodName, memberTypes,
                    bindingFlags, methods, parameterTypes, args, 1 /* limit */,
                    marshalFlags, ref methodIndexList, ref argsList,
                    ref argumentInfoList, ref errors) == ReturnCode.Ok)
            {
                if ((methodIndexList != null) && (methodIndexList.Count > 0) &&
                    (argsList != null) && (argsList.Count > 0))
                {
                    //
                    // FIXME: Select the first method that matches.  More
                    //        sophisticated logic may need to be added here
                    //        later.
                    //
                    methodIndex = methodIndexList[0];
                    args = argsList[0];

                    return ReturnCode.Ok;
                }
                else
                {
                    if (errors == null)
                        errors = new ResultList();

                    errors.Add("missing first method index or arguments in list");
                }
            }

            if (errors != null)
                error = errors;

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        [Obsolete()]
        private static ReturnCode AncientFindMethodAndFixupArguments( /* LEGACY */
            Interpreter interpreter,               /* in */
            IBinder binder,                        /* in */
            CultureInfo cultureInfo,               /* in */
            Type type,                             /* in */
            string objectName,                     /* in */
            string methodName,                     /* in */
            MemberTypes memberTypes,               /* in */
            BindingFlags bindingFlags,             /* in */
            MethodBase[] methods,                  /* in */
            TypeList parameterTypes,               /* in */
            MarshalFlags marshalFlags,             /* in */
            ref object[] args,                     /* in, out */
            ref int methodIndex,                   /* out */
            ref ArgumentInfoList argumentInfoList, /* out */
            ref Result error                       /* out */
            )
        {
            if (methods != null)
            {
                //
                // NOTE: At what method index, if any, did the method name and
                //       parameter info match (these are used to construct a more
                //       informative error message)?
                //
                string matchMethodName = null;

                //
                // NOTE: These variables are not always 100% accurate due to how
                //       we search among the overloads of a method.  They are
                //       provided to assist in constructing an error message on a
                //       "best effort" basis.  Another alternative is to keep a
                //       list of all errors encountered during each attempted
                //       "binding" to a particular overload of a method.
                //
                ParameterInfo[] matchParameterInfo = null; /* WARNING: Not 100% accurate. */
                int matchParameterIndex = Index.Invalid;   /* WARNING: Not 100% accurate. */
                int matchArgumentIndex = Index.Invalid;    /* WARNING: Not 100% accurate. */
                object matchArgumentValue = null;          /* WARNING: Not 100% accurate. */

                //
                // NOTE: Process each method info and attempt to match it against
                //       the supplied arguments taking into account the number and
                //       coerced types of the arguments, the (in, out) attributes,
                //       and the params argument, if any.
                //
                for (int matchIndex = 0; matchIndex < methods.Length; matchIndex++)
                {
                    //
                    // NOTE: Reset these error message helpers here because they
                    //       will no longer be accurate.
                    //
                    matchParameterInfo = null;

                    //
                    // NOTE: Get the method for the current method.
                    //
                    MethodBase method = methods[matchIndex];

                    if (method != null)
                    {
                        //
                        // NOTE: We need the name of the method to check.
                        //
                        string checkMethodName = methods[matchIndex].Name;

                        //
                        // NOTE: If the method name appears to be invalid, just skip it.
                        //
                        if (!String.IsNullOrEmpty(checkMethodName))
                        {
                            //
                            // NOTE: The method name must be matched first since we have
                            //       likely been passed all the methods for this type.
                            //
                            if (MatchMethodName(checkMethodName, methodName, memberTypes, bindingFlags))
                            {
                                //
                                // NOTE: Does the caller want to attempt to convert
                                //       arguments and filter on them?
                                //
                                if (args != null)
                                {
                                    //
                                    // NOTE: Set the name match index so that even if we ultimately
                                    //       do not find a matching method at least the error handling
                                    //       code knows that we got this far.
                                    //
                                    matchMethodName = checkMethodName;

                                    //
                                    // NOTE: We need the parameters of the method to check.
                                    //
                                    ParameterInfo[] checkParameterInfo = methods[matchIndex].GetParameters();

                                    //
                                    // NOTE: If the parameters appear to be invalid, just skip them.
                                    //
                                    if (checkParameterInfo != null)
                                    {
                                        int minimumCount = 0;
                                        int maximumCount = 0;

                                        if (GetParameterCounts(checkParameterInfo,
                                                ref minimumCount, ref maximumCount, ref error) == ReturnCode.Ok)
                                        {
                                            //
                                            // NOTE: Get the number of arguments supplied by the caller.
                                            //
                                            int argumentLength = args.Length;

                                            //
                                            // NOTE: Does the number of arguments supplied by the caller fit
                                            //       within the minimum and maximum number of arguments for
                                            //       this method?
                                            //
                                            if (((minimumCount == Count.Invalid) || (argumentLength >= minimumCount)) &&
                                                ((maximumCount == Count.Invalid) || (argumentLength <= maximumCount)))
                                            {
                                                //
                                                // NOTE: Check for any parameter type hints provided by the caller.
                                                //       If they were supplied then they must match up with the
                                                //       parameter types for this method.
                                                //
                                                if ((parameterTypes == null) || MatchParameterTypes(
                                                        parameterTypes, checkParameterInfo, true, true, ref error))
                                                {
                                                    //
                                                    // NOTE: Set the matching parameter info so that even if we
                                                    //       ultimately do not find a matching method at least the
                                                    //       error handling code knows that we got this far.
                                                    //
                                                    matchParameterInfo = checkParameterInfo;

                                                    //
                                                    // NOTE: Get the number of formal arguments for the method to
                                                    //       check.
                                                    //
                                                    int checkArgumentLength = checkParameterInfo.Length;

                                                    //
                                                    // NOTE: Create a temporary holding area for attempted argument
                                                    //       type conversions because in the case we do not fully
                                                    //       match the method we do not want to fiddle with the
                                                    //       caller's original arguments array.
                                                    //
                                                    object[] newArgs = new object[checkArgumentLength];

                                                    //
                                                    // NOTE: Start consuming the original arguments at the first
                                                    //       argument.
                                                    //
                                                    int argumentIndex = 0;

                                                    //
                                                    // NOTE: Reset these error message helpers here because they
                                                    //       will no longer be accurate.
                                                    //
                                                    matchParameterIndex = Index.Invalid;
                                                    matchArgumentIndex = Index.Invalid;
                                                    matchArgumentValue = null;

                                                    //
                                                    // NOTE: Process each formal argument, checking the various
                                                    //       constraints imposed by it upon the supplied argument
                                                    //       value.
                                                    //
                                                    for (int parameterIndex = 0; parameterIndex < checkArgumentLength; parameterIndex++)
                                                    {
                                                        //
                                                        // NOTE: Set the matching parameter and argument indexes so that
                                                        //       even if we ultimately do not find a matching method at
                                                        //       least the error handling code knows that we got this far.
                                                        //
                                                        matchParameterIndex = parameterIndex;
                                                        matchArgumentIndex = argumentIndex;

                                                        //
                                                        // NOTE: Check argument attributes to see if they are ref (in, out) or out (out).
                                                        //       Enforce argument being an existing script variable name for ref.
                                                        //       Enforce argument being a script variable name for out.
                                                        //
                                                        //       Also, arrays need to be handled in concert with (in, out).  Script arrays
                                                        //       will be used and the indexes will always be in the form (x) or (x,y,z)
                                                        //       where x is an integer index into a single or multi-dimensional array, and
                                                        //       y and z, etc are integer indexes of a multi-dimensional array.
                                                        //
                                                        ParameterInfo parameterInfo = checkParameterInfo[parameterIndex];

                                                        if (parameterInfo == null)
                                                            break; // BUGBUG: Better error message here.

                                                        Type parameterType = parameterInfo.ParameterType;
                                                        string parameterName = parameterInfo.Name;

                                                        //
                                                        // NOTE: Check if this parameter is of a pointer type.
                                                        //
                                                        if (parameterType.IsPointer)
                                                        {
                                                            //
                                                            // NOTE: We do not allow and cannot process pointer types.
                                                            //
                                                            break; // BUGBUG: Better error message here.
                                                        }
                                                        else if (parameterInfo.IsDefined(typeof(ParamArrayAttribute), false))
                                                        {
                                                            //
                                                            // NOTE: Inside the loop, we need the type of the elements in the params array.
                                                            //
                                                            Type elementType = parameterType.GetElementType();

                                                            //
                                                            // NOTE: For simplicity, we will add each supplied argument to the params list
                                                            //       and convert the whole thing to an array at the end of the loop (when
                                                            //       we place the params array into the new arguments array at the current
                                                            //       argument index).
                                                            //
                                                            ObjectList @params = new ObjectList();

                                                            //
                                                            // NOTE: Save off the starting argument index because that is where we need to
                                                            //       place the params array.
                                                            //
                                                            int paramsIndex = argumentIndex; // save for below.

                                                            //
                                                            // NOTE: Use all the remaining arguments for this params array.  None of these
                                                            //       are allowed to be ByRef, therefore, we just need to perform type
                                                            //       coercion on the remaining argument strings (which will all end up being
                                                            //       of the same type).
                                                            //
                                                            for (; argumentIndex < argumentLength; argumentIndex++)
                                                            {
                                                                //
                                                                // NOTE: Start with their original argument value.
                                                                //
                                                                matchArgumentValue = args[argumentIndex];

                                                                if (FixupArgument(
                                                                        interpreter, binder, cultureInfo, elementType,
                                                                        ArgumentInfo.Create(argumentIndex, elementType,
                                                                            parameterName, true, false), MarshalFlags.None,
                                                                        true, true, false, ref matchArgumentValue,
                                                                        ref error) == ReturnCode.Ok)
                                                                {
                                                                    @params.Add(matchArgumentValue);
                                                                }
                                                                else
                                                                {
                                                                    break;
                                                                }
                                                            }

                                                            //
                                                            // NOTE: Did we successfully complete the argument processing
                                                            //       loop above?  If so, we need to resize (expand or truncate)
                                                            //       the new arguments array because there may have been 0
                                                            //       supplied arguments or we may have just collapsed N supplied
                                                            //       arguments into 1 params argument.
                                                            //
                                                            if (argumentIndex == argumentLength)
                                                            {
                                                                //
                                                                // NOTE: Make sure that the params array argument is the final
                                                                //       argument.  This includes forcing the outer for loop to
                                                                //       recognize that we have matched this method and are
                                                                //       totally done processing the supplied arguments by
                                                                //       reducing the supplied argument count and setting the
                                                                //       argument index to match.
                                                                //
                                                                argumentLength = paramsIndex + 1;
                                                                argumentIndex = argumentLength;

                                                                //
                                                                // NOTE: Physically resize the new arguments array to exactly
                                                                //       match the argument count; whether it is larger or
                                                                //       smaller does not matter.
                                                                //
                                                                Array.Resize(ref newArgs, argumentLength);

                                                                //
                                                                // NOTE: Store the params list into the corresponding new
                                                                //       argument slot.
                                                                //
                                                                newArgs[paramsIndex] = Array.CreateInstance(elementType,
                                                                    @params.Count);

                                                                Array.Copy(@params.ToArray(), (Array)newArgs[paramsIndex],
                                                                    @params.Count);
                                                            }

                                                            //
                                                            // NOTE: We just used up the all supplied arguments (or
                                                            //       we encountered an error that prevents us from
                                                            //       continuing to process this method as a match).
                                                            //
                                                            break;
                                                        }
                                                        else
                                                        {
                                                            //
                                                            // FIXME: This is not 100% correct.  The .NET Framework and C#
                                                            //        are not consistent when it comes to setting the
                                                            //        parameter input and output properties.  More work
                                                            //        needs to be done to properly figure out the semantics
                                                            //        used by C#.
                                                            //
                                                            bool output = parameterType.IsByRef ||
                                                                parameterInfo.IsOut;
                                                            bool input = parameterInfo.IsIn ||
                                                                !parameterInfo.IsOut;
                                                            bool optional = parameterInfo.IsOptional;

                                                            //
                                                            // NOTE: Set the value of this argument.
                                                            //
                                                            if (argumentIndex < argumentLength)
                                                            {
                                                                //
                                                                // NOTE: Use the supplied value.
                                                                //
                                                                matchArgumentValue = args[argumentIndex];
                                                            }
                                                            else if (optional)
                                                            {
                                                                //
                                                                // NOTE: Use the default value (optional argument).
                                                                //
                                                                matchArgumentValue = parameterInfo.DefaultValue;
                                                            }
                                                            else
                                                            {
                                                                //
                                                                // NOTE: Use null.  If we get to this point, that should mean
                                                                //       that non-optional arguments are present after optional
                                                                //       ones; otherwise, we would have enough of the supplied
                                                                //       arguments to satisfy the formal arguments.
                                                                //
                                                                matchArgumentValue = null;
                                                            }

                                                            //
                                                            // NOTE: Set the argument value to convert.
                                                            //
                                                            newArgs[argumentIndex] = matchArgumentValue;

                                                            //
                                                            // NOTE: Attempt to convert the argument to the expected type.
                                                            //
                                                            if (FixupArgument(
                                                                    interpreter, binder, cultureInfo, parameterType,
                                                                    ArgumentInfo.Create(argumentIndex, parameterType,
                                                                        parameterName, input, output), marshalFlags,
                                                                    true, input, output, ref newArgs[argumentIndex],
                                                                    ref error) == ReturnCode.Ok)
                                                            {
                                                                if (output)
                                                                {
                                                                    if (argumentInfoList == null)
                                                                        argumentInfoList = new ArgumentInfoList();

                                                                    //
                                                                    // NOTE: Original args, not newArgs, because we need the
                                                                    //       variable name, not the value.
                                                                    //
                                                                    argumentInfoList.Add(ArgumentInfo.Create(
                                                                        argumentIndex, parameterType,
                                                                        (string)matchArgumentValue, input, output));
                                                                }
                                                            }
                                                            else
                                                            {
                                                                //
                                                                // NOTE: We could not convert the supplied argument to
                                                                //       the required type for this method.
                                                                //
                                                                break;
                                                            }

                                                            argumentIndex++;
                                                        }
                                                    }

                                                    //
                                                    // NOTE: Did we match up all arguments with their formal parameters?
                                                    //
                                                    if (argumentIndex == checkArgumentLength)
                                                    {
                                                        //
                                                        // NOTE: Commit changes to caller's original arguments.
                                                        //
                                                        Array.Resize(ref args, checkArgumentLength); /* NOTE: Might be NOP. */
                                                        Array.Copy(newArgs, args, checkArgumentLength);

                                                        methodIndex = matchIndex;
                                                        return ReturnCode.Ok;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    //
                                    // FIXME: Select the first method that matches the name (and
                                    //        the binding flags because the caller should have
                                    //        already performed that filtering step).  More
                                    //        sophisticated logic may need to be added here later.
                                    //
                                    methodIndex = matchIndex;
                                    return ReturnCode.Ok;
                                }
                            }
                        }
                    }
                }

                //
                // NOTE: Did we even find a method with a matching name
                //       and argument count?
                //
                if ((matchParameterInfo != null) &&
                    (matchParameterIndex != Index.Invalid) &&
                    (matchArgumentIndex != Index.Invalid))
                {
                    //
                    // WARNING: The argument value and parameter type are produced on a
                    //          "best effort" basis here because during our search we may
                    //          have overwritten them when we should not have (i.e. for a
                    //          subsequent method overload that had a matching number of
                    //          arguments).
                    //
                    error = String.Format(
                        "could not convert method {0} argument value \"{1}\" to type \"{2}\": {3}",
                        FormatOps.MethodOverload(Index.Invalid, objectName, methodName),
                        matchArgumentValue,
                        matchParameterInfo[matchParameterIndex].ParameterType.FullName,
                        error);
                }
                else if (matchMethodName != null)
                {
                    error = String.Format(
                        "no method {0} matches arguments: \"{1}\"",
                        FormatOps.MethodOverload(Index.Invalid, objectName, methodName),
                        error);
                }
                else
                {
                    error = String.Format(
                        "method {0} not found",
                        FormatOps.MethodOverload(Index.Invalid, objectName, methodName));
                }
            }
            else
            {
                error = "invalid method array";
            }

            return ReturnCode.Error;
        }
#endif
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetTypeListFromParameterInfo(
            ParameterInfo[] parameterInfo,
            bool strict,
            ref TypeList types,
            ref Result error
            )
        {
            if (parameterInfo != null)
            {
                int parameterLength = parameterInfo.Length;

                if (!strict || (parameterLength > 0))
                {
                    if (types == null)
                        types = new TypeList();

                    //
                    // NOTE: Iterate over all the parameters, adding the type
                    //       of the parameter to the resulting list.
                    //
                    for (int parameterIndex = 0; parameterIndex < parameterLength; parameterIndex++)
                    {
                        //
                        // NOTE: Just skip over invalid array entries.
                        //
                        if (parameterInfo[parameterIndex] != null)
                            types.Add(parameterInfo[parameterIndex].ParameterType);
                    }

                    return ReturnCode.Ok;
                }
                else
                {
                    error = "no parameters to process";
                }
            }
            else
            {
                error = "invalid parameter info";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool ComSucceeded(
            int hResult
            )
        {
            return (hResult >= 0);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static EventAttributes? GetEventAttributes(
            MemberInfo memberInfo
            )
        {
            return (memberInfo is EventInfo) ?
                ((EventInfo)memberInfo).Attributes : (EventAttributes?)null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static FieldAttributes? GetFieldAttributes(
            MemberInfo memberInfo
            )
        {
            return (memberInfo is FieldInfo) ?
                ((FieldInfo)memberInfo).Attributes : (FieldAttributes?)null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static string GetParameterName(
            int index,
            Type type
            )
        {
            return (type != null) ?
                String.Format("{0}{1}_{2}", ParameterNamePrefix, index, type.Name) :
                String.Format("{0}{1}", ParameterNamePrefix, index);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static object GetDefaultValue(
            Type type
            )
        {
            return ((type != null) && type.IsValueType) ?
                Activator.CreateInstance(type) : null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool DoesComObjectSupportInterface(
            IntPtr unknown,
            Type type
            )
        {
            if ((unknown != IntPtr.Zero) && (type != null))
            {
                IntPtr @interface = IntPtr.Zero;

                try
                {
                    Guid iid = type.GUID;

                    int result = Marshal.QueryInterface(unknown, ref iid,
                        out @interface);

                    if (ComSucceeded(result) &&
                        (@interface != IntPtr.Zero) && iid.Equals(type.GUID))
                    {
#if false // DEBUG && VERBOSE
                        TraceOps.DebugTrace(String.Format(
                            "{0}: DoesComObjectSupportInterface: succeeded, " +
                            "unknown = {1}, iid = {2}, type = {3}, result = " +
                            "{4}{5}", GlobalState.GetCurrentThreadId(), unknown,
                            iid, FormatOps.WrapOrNull(type), result,
                            Environment.NewLine), typeof(MarshalOps).Name);
#endif

                        return true;
                    }
                }
                finally
                {
                    if (@interface != IntPtr.Zero)
                    {
                        Marshal.Release(@interface);
                        @interface = IntPtr.Zero;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode GetTypeListFromComObject(
            Interpreter interpreter,
            string text,
            object @object,
            TypePairDictionary<string, long> interfaces,
            CultureInfo cultureInfo,
            ref TypeList types,
            ref Result error
            )
        {
            if (interpreter != null)
            {
                if (@object != null)
                {
                    if (interfaces != null)
                    {
#if NATIVE
                        string typeName1;
                        string typeName2;

                        /* IGNORED */
                        GetTypeNamesFromComObject(
                            @object, cultureInfo, out typeName1, out typeName2);
#endif

                        IntPtr unknown = IntPtr.Zero;

                        try
                        {
                            unknown = Marshal.GetIUnknownForObject(@object);

                            if (unknown != IntPtr.Zero)
                            {
#if COM_TYPE_CACHE
                                TypeList cacheTypes = null;

                                if (interpreter.GetCachedComTypeList(
                                        unknown, ref cacheTypes))
                                {
                                    foreach (Type cacheType in cacheTypes)
                                    {
                                        if (!interfaces.ContainsKey(cacheType) ||
                                            !DoesComObjectSupportInterface(unknown, cacheType))
                                        {
                                            //
                                            // NOTE: The cached type list is probably bogus,
                                            //       remove it now and continue with the
                                            //       normal lookup method.
                                            //
                                            /* IGNORED */
                                            interpreter.RemoveCachedComTypeList(unknown);

                                            goto notCached;
                                        }
                                    }

                                    types = cacheTypes;
                                    return ReturnCode.Ok;
                                }

                            notCached:
#endif

                                SortedDictionary<long, Type> sortedTypes =
                                    new SortedDictionary<long, Type>();

                                foreach (KeyValuePair<Type, IAnyPair<string, long>> pair
                                        in interfaces)
                                {
                                    Type type = pair.Key;

                                    if ((type != null) && type.IsInterface)
                                    {
                                        IAnyPair<string, long> anyPair = pair.Value;

                                        if (anyPair != null)
                                        {
                                            //
                                            // NOTE: Check if the COM object actually supports
                                            //       this interface.  Also, if possible, compare
                                            //       the short name of the type against the one
                                            //       returned by the object itself via
                                            //       IProvideClassInfo or IDispatch.  This seems
                                            //       to improve the matching when a COM object
                                            //       implements more than one interface.
                                            //
                                            if (DoesComObjectSupportInterface(unknown, type) &&
#if NATIVE
                                                (((typeName1 == null) ||
                                                String.Equals(type.Name, typeName1,
                                                    StringOps.SystemStringComparisonType)) ||
                                                ((typeName2 == null) ||
                                                String.Equals(type.Name, typeName2,
                                                    StringOps.SystemStringComparisonType)))
#else
                                                true
#endif
                                                )
                                            {
                                                sortedTypes.Add(anyPair.Y, type);
                                            }
                                        }
                                    }
                                }

                                if (sortedTypes.Count > 0)
                                {
                                    if (types == null)
                                        types = new TypeList();

                                    types.AddRange(sortedTypes.Values);

#if COM_TYPE_CACHE
                                    /* IGNORED */
                                    interpreter.AddCachedComTypeList(unknown, types);
#endif

                                    return ReturnCode.Ok;
                                }

                                TraceOps.DebugTrace(String.Format(
                                    "{0}: GetTypeListFromComObject: interface " +
                                    "type for COM object {1} ({2}) not found{3}",
                                    GlobalState.GetCurrentThreadId(),
                                    FormatOps.WrapOrNull(text), unknown,
                                    Environment.NewLine), typeof(MarshalOps).Name);

                                error = "type not found";
                            }
                            else
                            {
                                error = "invalid COM object";
                            }
                        }
                        catch (Exception e)
                        {
                            error = e;
                        }
                        finally
                        {
                            if (unknown != IntPtr.Zero)
                            {
                                Marshal.Release(unknown);
                                unknown = IntPtr.Zero;
                            }
                        }
                    }
                    else
                    {
                        error = "invalid interface list";
                    }
                }
                else
                {
                    error = "invalid object";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

#if NATIVE
        private static bool GetTypeInfoFromComObject(
            object @object,
            CultureInfo cultureInfo,
            out ITypeInfo typeInfo1,
            out ITypeInfo typeInfo2
            )
        {
            typeInfo1 = null;
            typeInfo2 = null;

            if (@object != null)
            {
                try
                {
                    UnsafeNativeMethods.IProvideClassInfo provideClassInfo =
                        @object as UnsafeNativeMethods.IProvideClassInfo;

                    if (provideClassInfo != null)
                        typeInfo1 = provideClassInfo.GetClassInfo();
                }
                catch
                {
                    // do nothing.
                }

                try
                {
                    UnsafeNativeMethods.IDispatch dispatch =
                        @object as UnsafeNativeMethods.IDispatch;

                    if (dispatch != null)
                        typeInfo2 = dispatch.GetTypeInfo(0,
                            (cultureInfo != null) ? cultureInfo.LCID : 0);
                }
                catch
                {
                    // do nothing.
                }

                return (typeInfo1 != null) || (typeInfo2 != null);
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool GetTypeNamesFromComObject(
            object @object,
            CultureInfo cultureInfo,
            out string typeName1,
            out string typeName2
            )
        {
            typeName1 = null;
            typeName2 = null;

            if (@object != null)
            {
                ITypeInfo typeInfo1 = null;
                ITypeInfo typeInfo2 = null;

                try
                {
                    GetTypeInfoFromComObject(
                        @object, cultureInfo, out typeInfo1, out typeInfo2);

                    if (typeInfo1 != null)
                    {
                        try
                        {
                            typeName1 = Marshal.GetTypeInfoName(typeInfo1);
                        }
                        catch
                        {
                            // do nothing.
                        }
                    }

                    if (typeInfo2 != null)
                    {
                        try
                        {
                            typeName2 = Marshal.GetTypeInfoName(typeInfo2);
                        }
                        catch
                        {
                            // do nothing.
                        }
                    }

#if false // DEBUG && VERBOSE
                    TraceOps.DebugTrace(String.Format(
                        "{0}: GetTypeNamesFromComObject: object = {1}, " +
                        "cultureInfo = {2}, typeName1 = {3}, typeName2 = {4}{5}",
                        GlobalState.GetCurrentThreadId(),
                        FormatOps.WrapOrNull(@object),
                        FormatOps.WrapOrNull(cultureInfo),
                        FormatOps.WrapOrNull(typeName1),
                        FormatOps.WrapOrNull(typeName2),
                        Environment.NewLine), typeof(MarshalOps).Name);
#endif

                    return (typeName1 != null) || (typeName2 != null);
                }
                finally
                {
                    if (typeInfo2 != null)
                    {
                        Marshal.ReleaseComObject(typeInfo2);
                        typeInfo2 = null;
                    }

                    if (typeInfo1 != null)
                    {
                        Marshal.ReleaseComObject(typeInfo1);
                        typeInfo1 = null;
                    }
                }
            }

            return false;
        }
#endif

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static Type GetTypeFromComObject(
            Interpreter interpreter,
            string text,
            object @object,
            TypePairDictionary<string, long> interfaces,
            CultureInfo cultureInfo,
            ref Result error
            )
        {
            TypeList types = null;

            if (GetTypeListFromComObject(interpreter,
                    text, @object, interfaces, cultureInfo,
                    ref types, ref error) == ReturnCode.Ok)
            {
                if (types != null)
                {
                    if (types.Count > 0)
                    {
                        if (types.Count > 1)
                        {
                            TraceOps.DebugTrace(String.Format(
                                "{0}: GetTypeFromComObject: interface type " +
                                "for COM object {1} is ambiguous between: {2}{3}",
                                GlobalState.GetCurrentThreadId(),
                                FormatOps.WrapOrNull(text), FormatOps.WrapOrNull(types),
                                Environment.NewLine), typeof(MarshalOps).Name);
                        }

                        return types[0];
                    }
                    else
                    {
                        error = "type not found";
                    }
                }
                else
                {
                    error = "invalid type list";
                }
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static MethodAttributes? GetMethodAttributes(
            MemberInfo memberInfo
            )
        {
            return (memberInfo is MethodBase) ?
                ((MethodBase)memberInfo).Attributes : (MethodAttributes?)null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static PropertyAttributes? GetPropertyAttributes(
            MemberInfo memberInfo
            )
        {
            return (memberInfo is PropertyInfo) ?
                ((PropertyInfo)memberInfo).Attributes : (PropertyAttributes?)null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static TypeAttributes? GetTypeAttributes(
            MemberInfo memberInfo
            )
        {
            return (memberInfo is Type) ?
                ((Type)memberInfo).Attributes : (TypeAttributes?)null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ParameterInfo GetReturnParameterInfo(
            MethodBase methodBase
            )
        {
            return (methodBase is MethodInfo) ? ((MethodInfo)methodBase).ReturnParameter : null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetMethodBaseFromMemberInfo(
            MemberInfo[] memberInfo,
            BindingFlags bindingFlags,
            ref MethodBase[] methodBase,
            ref Result error
            )
        {
            if (memberInfo != null)
            {
                int memberLength = memberInfo.Length;

                if (memberLength > 0)
                {
                    //
                    // NOTE: We have no idea how many methods we will find,
                    //       allocate an empty list and keep adding to it.
                    //
                    MethodBaseList methodBaseList = new MethodBaseList();

                    //
                    // NOTE: Iterate over all the members, adding the methods
                    //       we find verbatim and flattening the get and set
                    //       accessor methods for properties into the list
                    //       as well.
                    //
                    for (int memberIndex = 0; memberIndex < memberLength; memberIndex++)
                    {
                        //
                        // NOTE: Just skip over invalid array entries.
                        //
                        if (memberInfo[memberIndex] != null)
                        {
                            //
                            // NOTE: Is this member a method base (a ConstructorInfo,
                            //       MethodInfo, etc)?
                            //
                            if (memberInfo[memberIndex] is MethodBase)
                            {
                                methodBaseList.Add((MethodBase)memberInfo[memberIndex]);
                            }
                            //
                            // NOTE: Is this member an event?  If so, lookup all the
                            //       methods associated with it.
                            //
                            else if (memberInfo[memberIndex] is EventInfo)
                            {
                                MethodInfo[] methodInfo = null;

                                if (GetMethodInfoFromEventInfo(
                                        new EventInfo[] { (EventInfo)memberInfo[memberIndex] },
                                        bindingFlags, ref methodInfo, ref error) == ReturnCode.Ok)
                                {
                                    int methodLength = methodInfo.Length;

                                    for (int methodIndex = 0; methodIndex < methodLength; methodIndex++)
                                        if (methodInfo[methodIndex] != null)
                                            methodBaseList.Add(methodInfo[methodIndex]);
                                }
                                else
                                {
                                    return ReturnCode.Error;
                                }
                            }
                            //
                            // NOTE: Is this member a property?  If so, lookup the
                            //       accessor methods.
                            //
                            else if (memberInfo[memberIndex] is PropertyInfo)
                            {
                                MethodInfo[] methodInfo = null;

                                if (GetMethodInfoFromPropertyInfo(
                                        new PropertyInfo[] { (PropertyInfo)memberInfo[memberIndex] },
                                        bindingFlags, ref methodInfo, ref error) == ReturnCode.Ok)
                                {
                                    int methodLength = methodInfo.Length;

                                    for (int methodIndex = 0; methodIndex < methodLength; methodIndex++)
                                        if (methodInfo[methodIndex] != null)
                                            methodBaseList.Add(methodInfo[methodIndex]);
                                }
                                else
                                {
                                    return ReturnCode.Error;
                                }
                            }
                        }
                    }

                    //
                    // NOTE: Now that we have totally succeeded, place the
                    //       result into the caller's array.
                    //
                    methodBase = methodBaseList.ToArray();

                    return ReturnCode.Ok;
                }
                else
                {
                    error = "no members to process";
                }
            }
            else
            {
                error = "invalid member info";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetMethodInfoFromEventInfo(
            EventInfo[] eventInfo,
            BindingFlags bindingFlags,
            ref MethodInfo[] methodInfo,
            ref Result error
            )
        {
            if (eventInfo != null)
            {
                int eventLength = eventInfo.Length;

                if (eventLength > 0)
                {
                    //
                    // NOTE: Do we want to include non-public methods?
                    //
                    bool nonPublic =
                        FlagOps.HasFlags(bindingFlags, BindingFlags.NonPublic, true);

                    //
                    // NOTE: We have no idea how many methods we will find,
                    //       allocate an empty list and keep adding to it.
                    //
                    MethodInfoList methodInfoList = new MethodInfoList();

                    //
                    // NOTE: Iterate over all the properties, flattening the get and
                    //       set accessor methods into one method array.
                    //
                    for (int eventIndex = 0; eventIndex < eventLength; eventIndex++)
                    {
                        //
                        // NOTE: Just skip over invalid array entries.
                        //
                        if (eventInfo[eventIndex] != null)
                        {
                            //
                            // NOTE: Add the method used to add an event handler
                            //       delegate to this event source.
                            //
                            methodInfoList.Add(
                                eventInfo[eventIndex].GetAddMethod(nonPublic));

                            //
                            // NOTE: Add the methods that were associated with this
                            //       event in MSIL using the .other directive.
                            //
                            methodInfoList.AddRange(
                                eventInfo[eventIndex].GetOtherMethods(nonPublic));

                            //
                            // NOTE: Add the method that is called when this event is
                            //       raised.
                            //
                            methodInfoList.Add(
                                eventInfo[eventIndex].GetRaiseMethod(nonPublic));

                            //
                            // NOTE: Add the method used to remove an event handler
                            //       delegate from this event source.
                            //
                            methodInfoList.Add(
                                eventInfo[eventIndex].GetRemoveMethod(nonPublic));
                        }
                    }

                    //
                    // NOTE: Now that we have totally succeeded, place the
                    //       result into the caller's array.
                    //
                    methodInfo = methodInfoList.ToArray();

                    return ReturnCode.Ok;
                }
                else
                {
                    error = "no events to process";
                }
            }
            else
            {
                error = "invalid event info";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode GetMethodInfoFromPropertyInfo(
            PropertyInfo[] propertyInfo,
            BindingFlags bindingFlags,
            ref MethodInfo[] methodInfo,
            ref Result error
            )
        {
            if (propertyInfo != null)
            {
                int propertyLength = propertyInfo.Length;

                if (propertyLength > 0)
                {
                    //
                    // NOTE: Do we want to include non-public methods?
                    //
                    bool nonPublic =
                        FlagOps.HasFlags(bindingFlags, BindingFlags.NonPublic, true);

                    //
                    // NOTE: Allocate enough space to hold get and set methods
                    //       for every property.
                    //
                    methodInfo = new MethodInfo[propertyLength * 2];

                    //
                    // NOTE: Iterate over all the properties, flattening the get and
                    //       set accessor methods into one method array.
                    //
                    for (int propertyIndex = 0; propertyIndex < propertyLength; propertyIndex++)
                    {
                        //
                        // NOTE: Just skip over invalid array entries.
                        //
                        if (propertyInfo[propertyIndex] != null)
                        {
                            //
                            // NOTE: Populate the get method for this property, this
                            //       may end up being null; however, we do not care
                            //       about that at this point.
                            //
                            methodInfo[propertyIndex * 2] =
                                propertyInfo[propertyIndex].GetGetMethod(nonPublic);

                            //
                            // NOTE: Populate the set method for this property, this
                            //       may end up being null; however, we do not care
                            //       about that at this point.
                            //
                            methodInfo[(propertyIndex * 2) + 1] =
                                propertyInfo[propertyIndex].GetSetMethod(nonPublic);
                        }
                    }

                    return ReturnCode.Ok;
                }
                else
                {
                    error = "no properties to process";
                }
            }
            else
            {
                error = "invalid property info";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool MatchMethodName(
            string checkMethodName,
            string methodName,
            MemberTypes memberTypes,
            BindingFlags bindingFlags
            )
        {
            if (String.IsNullOrEmpty(methodName))
            {
                //
                // NOTE: Match anything if they are checking for a null
                //       or empty member name.
                //
                return true;
            }
            else
            {
                if (!String.IsNullOrEmpty(checkMethodName))
                {
                    //
                    // NOTE: If the binding flags say to ignore case, then we will.
                    //
                    StringComparison comparisonType =
                        FlagOps.HasFlags(bindingFlags, BindingFlags.IgnoreCase, true) ?
                            StringOps.UserNoCaseStringComparisonType :
                            StringOps.UserStringComparisonType;

                    //
                    // NOTE: Attempt an exact match first.
                    //
                    if (String.Compare(checkMethodName, methodName, comparisonType) == 0)
                    {
                        //
                        // NOTE: Exact match of the member name.
                        //
                        return true;
                    }
                    else if (FlagOps.HasFlags(memberTypes, MemberTypes.Property, true) &&
                        (AccessorPrefixes != null))
                    {
                        //
                        // HACK: For properties only, we allow the removal of the leading
                        //       "get_" and "set_" from the member name so that we can
                        //       properly match the accessors (without knowing in advance
                        //       whether we actually want the get or set accessor).
                        //
                        // BUGFIX: We want to be sure and use the correct comparison
                        //         length here.
                        //
                        foreach (string accessorPrefix in AccessorPrefixes)
                        {
                            if (accessorPrefix == null)
                                continue;

                            if (!checkMethodName.StartsWith(
                                    accessorPrefix, comparisonType))
                            {
                                continue;
                            }

                            int accessorPrefixLength = accessorPrefix.Length;

                            int maximumLength = Math.Max(
                                checkMethodName.Length - accessorPrefixLength,
                                methodName.Length);

                            if (String.Compare(checkMethodName, accessorPrefixLength,
                                    methodName, 0, maximumLength, comparisonType) == 0)
                            {
                                //
                                // NOTE: Exact match of the member name without the
                                //       get/set accessor prefix.
                                //
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode GetParameterCounts(
            ParameterInfo[] parameterInfo,
            ref int minimumCount,
            ref int maximumCount,
            ref Result error
            )
        {
            if (parameterInfo != null)
            {
                minimumCount = 0;
                maximumCount = 0;

                for (int parameterIndex = 0; parameterIndex < parameterInfo.Length; parameterIndex++)
                {
                    //
                    // NOTE: Just skip over invalid array entries.
                    //
                    if (parameterInfo[parameterIndex] != null)
                    {
                        if (parameterInfo[parameterIndex].IsDefined(typeof(ParamArrayAttribute), false))
                        {
                            //
                            // NOTE: The minimum [required] number of parameters
                            //       is unchanged and the maximum [allowed]
                            //       number of parameters is now "infinite".
                            //
                            maximumCount = Count.Invalid;
                        }
                        else
                        {
                            //
                            // NOTE: If the parameter is optional then it does not
                            //       increase the minimum number of required
                            //       parameters.
                            //
                            if (!parameterInfo[parameterIndex].IsOptional)
                            {
                                //
                                // NOTE: We hit another required parameter, increase
                                //       the minimum [required] number of parameters.
                                //
                                minimumCount++;
                            }

                            //
                            // NOTE: If the maximum [allowed] number of parameters
                            //       has not already been set to "infinite",
                            //       increase it.
                            //
                            if (maximumCount != Count.Invalid)
                                maximumCount++;
                        }
                    }
                }

                return ReturnCode.Ok;
            }
            else
            {
                error = "invalid parameter info";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsPrimitiveType(
            Type type,
            bool output
            )
        {
            Type elementType = null;

            return IsPrimitiveType(type, output, ref elementType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsPrimitiveType(
            Type type,
            bool output,
            ref Type elementType
            )
        {
            if (type != null)
            {
                if (type.IsPrimitive)
                {
                    elementType = type;

                    return true;
                }
                else if (output && type.IsByRef)
                {
                    Type byRefElementType = type.GetElementType();

                    if ((byRefElementType != null) &&
                        byRefElementType.IsPrimitive)
                    {
                        elementType = byRefElementType;

                        return true;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsEnumType(
            Type type,
            bool nullable,
            bool output
            )
        {
            Type elementType = null;

            return IsEnumType(type, nullable, output, ref elementType);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsEnumType(
            Type type,
            bool nullable,
            bool output,
            ref Type elementType
            )
        {
            if (type != null)
            {
                if (type.IsEnum)
                {
                    elementType = type;

                    return true;
                }
                else
                {
                    Type valueType = null;

                    if (nullable &&
                        IsNullableType(type, output, ref valueType) &&
                        (valueType != null) && valueType.IsEnum)
                    {
                        elementType = valueType;

                        return true;
                    }
                    else if (output && type.IsByRef)
                    {
                        Type byRefElementType = type.GetElementType();

                        if ((byRefElementType != null) &&
                            byRefElementType.IsEnum)
                        {
                            elementType = byRefElementType;

                            return true;
                        }
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSimpleTypeForToString(
            IBinder binder,
            Type type
            )
        {
            if (type != null)
            {
                if (type.IsPrimitive || type.IsEnum)
                {
                    return true;
                }
                else
                {
                    IScriptBinder scriptBinder = binder as IScriptBinder;

                    if ((scriptBinder != null) &&
                        scriptBinder.HasChangeTypeCallback(type, true))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // NOTE: This method returns non-zero for all the types that can be
        //       converted by the default script binder to a StringList object.
        //
        private static bool IsStringListForChangeType(
            IBinder binder,
            Type type
            )
        {
            if (binder != null)
            {
                IScriptBinder scriptBinder = binder as IScriptBinder;

                if (scriptBinder != null)
                {
                    ChangeTypeCallback callback = null;

                    if (scriptBinder.HasChangeTypeCallback(
                            type, false, ref callback) &&
                        scriptBinder.IsCoreStringListChangeTypeCallback(
                            callback))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSimpleTypeForToString(
            Type type
            )
        {
            return (type != null) &&
                ((type == typeof(string)) ||
                    (type == typeof(StringPair)) ||
                    (type == typeof(StringList)));
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsGenericListType(
            IBinder binder, /* NOT USED */
            Type type
            )
        {
            while ((type != null) && (type != typeof(object)))
            {
                if (type.IsGenericType &&
                    (type.GetGenericTypeDefinition() == typeof(List<>)))
                {
                    return true;
                }

                type = type.BaseType;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static Type GetRuntimeType()
        {
            return RuntimeType;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool GetGenericTypeInfo(
            Type type,
            int levels,
            Dictionary<Type, int> types,
            ref Type genericType,
            ref Type[] genericArguments
            )
        {
            do
            {
                //
                // NOTE: Return false if the type is invalid, is at the very
                //       root of the type hierarchy, or contains any generic
                //       parameters.
                //
                if ((type == null) || (type == typeof(object) ||
                    type.ContainsGenericParameters))
                {
                    return false;
                }

                //
                // NOTE: Keep track of the fact that we have seen this type
                //       [before we change it to the base type].
                //
                if (!types.ContainsKey(type))
                    types.Add(type, levels);

                //
                // NOTE: Return true if the type is generic.  Also, fetch the
                //       generic type definition and generic arguments into the
                //       parameters provided by the caller.
                //
                if (type.IsGenericType)
                {
                    genericType = type.GetGenericTypeDefinition();
                    genericArguments = type.GetGenericArguments();

                    //
                    // NOTE: Only return true if the generic type definition
                    //       and generic arguments are actually valid.
                    //
                    if ((genericType != null) && (genericArguments != null))
                        return true;
                    else
                        return false;
                }

                type = type.BaseType;
            }
            while (true);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSimpleGenericTypeForToString(
            IBinder binder,
            Type type
            )
        {
            int levels = 0;
            Dictionary<Type, int> types = null;

            return IsSimpleGenericTypeForToString(
                binder, type, ref levels, ref types);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool IsSimpleGenericTypeForToString(
            IBinder binder,
            Type type,
            ref int levels,
            ref Dictionary<Type, int> types
            )
        {
            if ((type != null) && !type.ContainsGenericParameters)
            {
                //
                // NOTE: If the number of type nesting levels exceeds
                //       the arbitrary limit we have set then this is
                //       not a "simple" generic type; therefore, return
                //       false.
                //
                if (levels++ > MaximumTypeLevels)
                    return false;

                if (types == null)
                    types = new Dictionary<Type, int>();

                Type genericType = null;
                Type[] genericArguments = null;

                if (GetGenericTypeInfo(type, levels, types,
                        ref genericType, ref genericArguments))
                {
                    foreach (Type genericArgument in genericArguments)
                    {
                        //
                        // NOTE: If the generic argument is null,
                        //       something is really wrong.  In this
                        //       case, just return false.
                        //
                        if (genericArgument == null)
                            return false;

                        //
                        // BUGFIX: If we have already seen this type,
                        //         then it is certainly not a "simple"
                        //         generic type (i.e. it is nested
                        //         and contains one or more circular
                        //         type references).
                        //
                        if (types.ContainsKey(genericArgument))
                            return false;

                        //
                        // NOTE: Keep track of the fact that we have
                        //       seen this type.
                        //
                        types.Add(genericArgument, levels);

                        //
                        // NOTE: Make sure the generic argument is a simple
                        //       string type, primitive type (e.g. int), or
                        //       a simple generic type (e.g. List<int>);
                        //       otherwise, return false.
                        //
                        if (!IsSimpleTypeForToString(genericArgument) &&
                            !IsSimpleTypeForToString(binder, genericArgument) &&
                            !IsSimpleGenericTypeForToString(binder, genericArgument,
                                ref levels, ref types))
                        {
                            return false;
                        }
                    }

                    return true;
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode ConvertValueToString(
            IScriptBinder scriptBinder, /* in */
            CultureInfo cultureInfo,    /* in */
            Type type,                  /* in */
            OptionDictionary options,   /* in */
            object value,               /* in */
            bool create,                /* in */
            ref Result result           /* out */
            )
        {
            if (!create)
            {
                if ((scriptBinder != null) &&
                    scriptBinder.HasToStringCallback(type, false))
                {
                    IChangeTypeData changeTypeData = new ChangeTypeData(
                        "MarshalOps.ConvertValueToString", type, value,
                        options, cultureInfo, null, false);

                    ReturnCode code = scriptBinder.ToString(
                        changeTypeData, ref result);

                    if (code == ReturnCode.Ok)
                        result = changeTypeData.NewValue as string;

                    return code;
                }

                result = String.Format(
                    "binder ToString conversion not available for type \"{0}\"",
                    type);
            }
            else
            {
                result = "binder ToString conversion bypassed by caller";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static string GetObjectHandleName(
            Interpreter interpreter,
            string objectName,
            Assembly assembly
            )
        {
            return (objectName != null) ? objectName : FormatOps.AssemblyName(
                assembly, (interpreter != null) ? interpreter.NextId() :
                GlobalState.NextId());
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static string GetObjectHandleName(
            Interpreter interpreter,
            string objectName,
            Type valueType,
            bool runtime
            )
        {
            return (objectName != null) ? objectName : FormatOps.ObjectHandle(
                null, FormatOps.ObjectHandleTypeName(valueType, !runtime),
                (interpreter != null) ? interpreter.NextId() :
                GlobalState.NextId());
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static void CheckReturnValueType(
            Interpreter interpreter,
            string objectName,
            object value,
            Type valueType,
            bool runtime,
            ref ObjectFlags objectFlags,
            ref string localObjectName
            )
        {
            if (value is Assembly)
            {
                //
                // NOTE: Specially format the string handle so that it can be
                //       easily recognized as an assembly name.
                //
                // BUGBUG: When we format the name here for a given assembly it
                //         is always the same.  This can lead to problems if a
                //         script removes the assembly via [object dispose] and
                //         then tries to re-load it into the same variable as
                //         before (unless the variable has been unset in the
                //         interim) because the added reference is canceled out
                //         since the old and new values are the same.  An easy
                //         "fix" for this problem would be to include a unique
                //         Id at the end of the opaque object handle for
                //         assemblies.
                //
                // HACK: The "fix" mentioned above is now in place; however, it
                //       is ugly.
                //
                localObjectName = GetObjectHandleName(interpreter, objectName,
                    (Assembly)value);

                return;
            }

            ///////////////////////////////////////////////////////////////////

            if (value is IInterpreter)
            {
                //
                // NOTE: Set the flags that indicate that we know this is an
                //       Interpreter object (informational).
                //
                objectFlags |= ObjectFlags.Interpreter;

                //
                // NOTE: If this object represents the active interpreter, it
                //       cannot be disposed from a script without causing
                //       serious problems.  Set the flag accordingly.
                //
                if (Object.ReferenceEquals(value, interpreter))
                    objectFlags |= ObjectFlags.NoDispose;
            }
            else if (value is IHost)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       Host object (informational).
                //
                objectFlags |= ObjectFlags.Host;

                //
                // NOTE: If this object represents the active interpreter
                //       host, it cannot be disposed from a script without
                //       causing serious problems.  Set the flags accordingly.
                //
                if (Object.ReferenceEquals(value, interpreter.Host))
                    objectFlags |= ObjectFlags.NoDispose;
            }
#if DEBUGGER
            else if (value is IDebugger)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       Debugger object (informational).
                //
                objectFlags |= ObjectFlags.Debugger;

                //
                // NOTE: If this object represents the active script debugger,
                //       it cannot be disposed from a script without causing
                //       serious problems.  Set the flag accordingly.
                //
                if (Object.ReferenceEquals(value, interpreter.Debugger))
                    objectFlags |= ObjectFlags.NoDispose;
            }
#endif
            else if (value is IEventManager)
            {
                //
                // NOTE: Set the flags that indicate that we know this is an
                //       EventManager object (informational).
                //
                objectFlags |= ObjectFlags.EventManager;

                //
                // NOTE: If this object represents the active event manager,
                //       it cannot be disposed from a script without causing
                //       serious problems.  Set the flag accordingly.
                //
                if (Object.ReferenceEquals(value, interpreter.EventManager))
                    objectFlags |= ObjectFlags.NoDispose;
            }
#if THREADING
            else if (value is IContextManager)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       ContextManager object (informational).
                //
                objectFlags |= ObjectFlags.ContextManager;

                //
                // NOTE: If this object represents the active context manager,
                //       it cannot be disposed from a script without causing
                //       serious problems.  Set the flag accordingly.
                //
                if (Object.ReferenceEquals(
                        value, interpreter.InternalContextManager))
                {
                    objectFlags |= ObjectFlags.NoDispose;
                }
            }
            else if (value is IEngineContext)
            {
                //
                // NOTE: Set the flags that indicate that we know this is an
                //       EngineContext object (informational).
                //
                objectFlags |= ObjectFlags.EngineContext;

                //
                // NOTE: If this object represents an active engine context,
                //       it cannot be disposed from a script without causing
                //       serious problems.  However, there is no easy way to
                //       check if it belongs to this interpreter.  Set the
                //       flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }
            else if (value is IInteractiveContext)
            {
                //
                // NOTE: Set the flags that indicate that we know this is an
                //       InteractiveContext object (informational).
                //
                objectFlags |= ObjectFlags.InteractiveContext;

                //
                // NOTE: If this object represents an active interactive
                //       context, it cannot be disposed from a script without
                //       causing serious problems.  However, there is no easy
                //       way to check if it belongs to this interpreter.  Set
                //       the flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }
            else if (value is ITestContext)
            {
                //
                // NOTE: Set the flags that indicate that we know this is an
                //       TestContext object (informational).
                //
                objectFlags |= ObjectFlags.TestContext;

                //
                // NOTE: If this object represents an active test context,
                //       it cannot be disposed from a script without causing
                //       serious problems.  However, there is no easy way to
                //       check if it belongs to this interpreter.  Set the
                //       flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }
            else if (value is IVariableContext)
            {
                //
                // NOTE: Set the flags that indicate that we know this is an
                //       VariableContext object (informational).
                //
                objectFlags |= ObjectFlags.VariableContext;

                //
                // NOTE: If this object represents an active variable context,
                //       it cannot be disposed from a script without causing
                //       serious problems.  However, there is no easy way to
                //       check if it belongs to this interpreter.  Set the
                //       flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }
#endif
            else if (value is INamespace)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       Namespace object (informational).
                //
                objectFlags |= ObjectFlags.Namespace;

                //
                // NOTE: If this object represents an active namespace, it
                //       cannot be disposed from a script without causing
                //       serious problems.  Set the flag accordingly.
                //
                if (Object.ReferenceEquals(
                        ((INamespace)value).Interpreter, interpreter))
                {
                    objectFlags |= ObjectFlags.NoDispose;
                }
            }
            else if (value is ICallFrame)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       CallFrame object (informational).
                //
                objectFlags |= ObjectFlags.CallFrame;

                //
                // NOTE: If this object represents the active call frame, it
                //       cannot be disposed from a script without causing
                //       serious problems.  However, there is no easy way to
                //       check if it belongs to this interpreter.  Set the
                //       flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }
            else if (value is IWrapper)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       Wrapper object (informational).
                //
                objectFlags |= ObjectFlags.Wrapper;

                //
                // NOTE: If this object represents the active namespace, it
                //       cannot be disposed from a script without causing
                //       serious problems.  However, there is no easy way to
                //       check if it belongs to this interpreter.  Set the
                //       flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }
            else if (value is CallStack)
            {
                //
                // NOTE: Set the flags that indicate that we know this is a
                //       CallStack object (informational).
                //
                objectFlags |= ObjectFlags.CallStack;

                //
                // NOTE: If this object represents the active call stack, it
                //       cannot be disposed from a script without causing
                //       serious problems.  However, there is no easy way to
                //       check if it belongs to this interpreter.  Set the
                //       flag anyhow.
                //
                objectFlags |= ObjectFlags.NoDispose;
            }

            //
            // NOTE: If necessary, set the flags that indicate that we know
            //       this is a runtime object from this library.
            //
            if (runtime)
                objectFlags |= ObjectFlags.Runtime;

            //
            // NOTE: Return the [full] name of the object (i.e. include
            //       the namespace if the object is not from this runtime
            //       library).
            //
            localObjectName = GetObjectHandleName(interpreter, objectName,
                valueType, runtime);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode FixupReturnValue(
            Interpreter interpreter,           /* in */
            Type type,                         /* in */
            ObjectFlags flags,                 /* in */
            ObjectOptionType objectOptionType, /* in */
            string objectName,                 /* in */
            object value,                      /* in */
            bool create,                       /* in */
            bool alias,                        /* in */
            bool aliasReference,               /* in */
            ref Result result                  /* out */
            )
        {
            IBinder binder = null;
            CultureInfo cultureInfo = null;

            if (interpreter != null)
            {
                if (!FlagOps.HasFlags(flags, ObjectFlags.NoBinder, true))
                    binder = interpreter.Binder;

                cultureInfo = interpreter.CultureInfo;
            }

            return FixupReturnValue(
                interpreter, binder, cultureInfo, type, flags,
                alias ? ObjectOps.GetInvokeOptions(objectOptionType) : null,
                objectOptionType, objectName, null, value, create, true, alias,
                aliasReference, false, ref result);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode FixupReturnValue(
            Interpreter interpreter,           /* in */
            IBinder binder,                    /* in */
            CultureInfo cultureInfo,           /* in */
            Type type,                         /* in */
            ObjectFlags objectFlags,           /* in */
            OptionDictionary options,          /* in */
            ObjectOptionType objectOptionType, /* in */
            string objectName,                 /* in */
            string interpName,                 /* in */
            object value,                      /* in */
            bool create,                       /* in */
            bool dispose,                      /* in */
            bool alias,                        /* in */
            bool aliasReference,               /* in */
            bool toString,                     /* in */
            ref Result result                  /* out */
            )
        {
            ReturnCode code;
            string localObjectName = null;
            string aliasName = null;
            long token = 0;
            IAlias newAlias = null;

            if (interpreter != null)
            {
                if (!create && (value == null))
                {
                    result = String.Empty;
                    code = ReturnCode.Ok;
                }
                else if (!create && (value is string))
                {
                    result = (string)value;
                    code = ReturnCode.Ok;
                }
                else
                {
                    //
                    // BUGFIX: The type of null is "object".
                    //
                    Type valueType = (value != null) ? value.GetType() : typeof(object);

                    //
                    // NOTE: Fetch the object flags from the attribute on the
                    //       associated type, if any.
                    //
                    if (!FlagOps.HasFlags(
                            objectFlags, ObjectFlags.NoAttribute, true))
                    {
                        objectFlags |= AttributeOps.GetObjectFlags(valueType);
                    }

                    //
                    // HACK: Make COM Interop objects work [slightly better].
                    //
                    if (FlagOps.HasFlags(
                            objectFlags, ObjectFlags.NoComObjectReturn, true) ||
                        !IsSystemComObjectType(valueType) ||
                        ((valueType = GetTypeFromComObject(
                            interpreter, null, value, interpreter.ObjectInterfaces,
                            cultureInfo, ref result)) != null))
                    {
                        IScriptBinder scriptBinder = binder as IScriptBinder;
                        ReturnCode localCode;
                        Result localResult = null;

                        if (!create && IsEnumType(valueType, true, true))
                        {
                            result = (value != null) ? value.ToString() : String.Empty;
                            code = ReturnCode.Ok;
                        }
                        else if (!create && IsSimpleTypeForToString(valueType))
                        {
                            result = (value != null) ? value.ToString() : String.Empty;
                            code = ReturnCode.Ok;
                        }
                        else if (!create && IsSimpleTypeForToString(binder, valueType))
                        {
                            localCode = ConvertValueToString(scriptBinder, cultureInfo,
                                valueType, options, value, create, ref localResult);

                            if (localCode == ReturnCode.Ok)
                                result = localResult;
                            else
                                result = (value != null) ? value.ToString() : String.Empty;

                            code = ReturnCode.Ok;
                        }
                        else if (!create && IsSimpleGenericTypeForToString(binder, valueType) &&
                            IsGenericListType(binder, valueType))
                        {
                            result = (value != null) ? value.ToString() : String.Empty;
                            code = ReturnCode.Ok;
                        }
                        else if ((valueType != null) && valueType.IsPointer)
                        {
                            result = "pointer types are not supported";
                            code = ReturnCode.Error;
                        }
                        else
                        {
                            Type elementType = null;

                            if (!create &&
                                (IsOneDimensionalArrayType(valueType, ref elementType) ||
                                    HasElementType(valueType, false, ref elementType)) &&
                                (IsSimpleTypeForToString(elementType) ||
                                    IsSimpleTypeForToString(binder, elementType) ||
                                    (IsSimpleGenericTypeForToString(binder, elementType) &&
                                        IsGenericListType(binder, elementType))))
                            {
                                //
                                // NOTE: Convert all one-dimensional arrays of objects that have
                                //       a primitive, yet meaningful, string representation to a
                                //       simple list of strings.
                                //
                                result = new StringList(value);
                                code = ReturnCode.Ok;
                            }
                            else
                            {
                                //
                                // NOTE: Make sure that we use any existing opaque object handle for this
                                //       object value if possible; otherwise, we end up with a bunch of
                                //       opaque object handles pointing to the exact same object.  We
                                //       cannot reuse the opaque object value if the we need an alias and
                                //       it does not include one.
                                //
                                bool ignoreAlias = FlagOps.HasFlags(
                                    objectFlags, ObjectFlags.IgnoreAlias, true);

                                IObject @object = null;

                                if (!FlagOps.HasFlags(objectFlags, ObjectFlags.ForceNew, true) &&
                                    ((objectName == null) || /* NOTE: Only if not a specific name. */
                                    FlagOps.HasFlags(objectFlags, ObjectFlags.AllowExisting, true)) &&
                                    interpreter.GetObject(value,
                                        alias ? LookupFlags.MarshalAlias : LookupFlags.MarshalDefault,
                                        !ignoreAlias && alias ? ObjectFlags.Alias : ObjectFlags.None,
                                        !ignoreAlias && !alias ? ObjectFlags.Alias : ObjectFlags.None,
                                        true, true, ref localObjectName, ref @object) == ReturnCode.Ok)
                                {
                                    //
                                    // NOTE: Does the caller want to override the standard semantics
                                    //       of returning the object handle from the interpreter?  In
                                    //       that case, we will return the result of the ToString method
                                    //       on the object.
                                    //
                                    if (toString)
                                        result = (value != null) ? value.ToString() : String.Empty;
                                    else
                                        result = localObjectName;

                                    code = ReturnCode.Ok;
                                }
                                else
                                {
                                    localCode = ConvertValueToString(scriptBinder, cultureInfo,
                                        valueType, options, value, create, ref localResult);

                                    if (localCode == ReturnCode.Ok)
                                    {
                                        result = localResult;
                                        code = ReturnCode.Ok;
                                    }
                                    else
                                    {
                                        //
                                        // NOTE: Ok, we really haven't seen this object yet.  Add a new opaque
                                        //       object handle for this object value and return it.
                                        //
                                        bool runtime = (valueType != null) &&
                                            GlobalState.IsAssembly(valueType.Assembly);

                                        //
                                        // NOTE: Based on the type of the value to be returned, determine the
                                        //       opaque object handle name and the associated object flags.
                                        //
                                        CheckReturnValueType(
                                            interpreter, objectName, value, valueType, runtime,
                                            ref objectFlags, ref localObjectName);

                                        //
                                        // NOTE: Does the caller want to override the standard semantics
                                        //       of adding an object to the interpreter?  In that case,
                                        //       we will return the result of the ToString method on the
                                        //       object and then dispose of the object, since it represents
                                        //       a transient value at this point (the marshaller "owns" it
                                        //       and we can be relatively certain no other outside references
                                        //       to it exist).
                                        //
                                        if (toString)
                                        {
                                            result = (value != null) ? value.ToString() : String.Empty;
                                            code = ReturnCode.Ok;

                                            goto disposeObject;
                                        }
                                        else
                                        {
                                            if (alias)
                                                objectFlags |= ObjectFlags.Alias;

                                            //
                                            // NOTE: If the caller is not intentionally creating an opaque
                                            //       object handle, prevent it from being automatically
                                            //       disposed.
                                            //
                                            if (!create && !FlagOps.HasFlags(
                                                    objectFlags, ObjectFlags.AutoDispose, true) &&
                                                !FlagOps.HasFlags(objectOptionType,
                                                    ObjectOptionType.CreateOptionMask, false))
                                            {
                                                objectFlags |= ObjectFlags.NoAutoDispose;
                                            }

                                            code = interpreter.AddObject(
                                                localObjectName, type, objectFlags, ClientData.Empty, 0,
#if NATIVE && TCL
                                                interpName,
#endif
#if DEBUGGER && DEBUGGER_ARGUMENTS
                                                Engine.GetDebuggerExecuteArguments(interpreter),
#endif
                                                value, ref token, ref result);

                                            if (code == ReturnCode.Ok)
                                            {
                                                if (alias)
                                                {
                                                    aliasName = interpreter.GetObjectAliasName(
                                                        valueType, localObjectName, null);

                                                    code = interpreter.AddObjectAlias(
                                                        localObjectName, aliasName, options,
                                                        objectOptionType, aliasReference,
                                                        ref newAlias, ref result);

                                                    if (code == ReturnCode.Ok)
                                                    {
                                                        if (interpName != null)
                                                        {
#if NATIVE && TCL
                                                            code = interpreter.AddTclBridge(
                                                                newAlias as IExecute, interpName,
                                                                localObjectName, ClientData.Empty,
                                                                FlagOps.HasFlags(objectFlags,
                                                                    ObjectFlags.ForceDelete, true),
                                                                FlagOps.HasFlags(objectFlags,
                                                                    ObjectFlags.NoComplain, true),
                                                                ref result);
#else
                                                            result = "option \"-tcl\" not supported for this platform";
                                                            code = ReturnCode.Error;
#endif
                                                        }

                                                        if (code == ReturnCode.Ok)
                                                        {
                                                            //
                                                            // NOTE: The object, alias, and bridged Tcl
                                                            //       command (if any) were successfully
                                                            //       added to the interpreter.
                                                            //
                                                            result = localObjectName;

                                                            if (result != null)
                                                                result.ValueData = ObjectValueData;
                                                        }
#if NATIVE && TCL
                                                        else
                                                        {
                                                            goto removeAlias;
                                                        }
#endif
                                                    }
                                                    else
                                                    {
                                                        goto removeObject;
                                                    }
                                                }
                                                else
                                                {
                                                    result = localObjectName;

                                                    if (result != null)
                                                        result.ValueData = ObjectValueData;
                                                }
                                            }
                                            else
                                            {
                                                goto disposeObject;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        code = ReturnCode.Error;
                    }
                }
            }
            else
            {
                result = "invalid interpreter";
                code = ReturnCode.Error;
            }

            goto done;

#if NATIVE && TCL
        removeAlias:
            //
            // NOTE: If we get here, we failed to add the bridged Tcl command
            //       for the added object; therefore, undo our previously
            //       successful actions by removing the alias, removing the
            //       object, and then disposing it.
            //
            if (newAlias != null)
            {
                ReturnCode removeCode;
                Result removeResult = null;

                removeCode = interpreter.RemoveAliasAndCommand(
                    (aliasName != null) ? aliasName : localObjectName,
                    null, false, ref removeResult);

                if (removeCode != ReturnCode.Ok)
                    DebugOps.Complain(interpreter, removeCode, removeResult);
            }
#endif

        removeObject:
            //
            // NOTE: If we get here, we failed to add an alias for the added
            //       object; therefore, undo our previously successful actions
            //       by removing the object and then disposing it.
            //
            if (token != 0)
            {
                ReturnCode removeCode;
                Result removeResult = null;

                removeCode = interpreter.RemoveObject(
                    token, null, ObjectOps.GetDefaultSynchronous(),
                    ref dispose, ref removeResult);

                if (removeCode != ReturnCode.Ok)
                    DebugOps.Complain(interpreter, removeCode, removeResult);
            }

            goto done;

        disposeObject:
            //
            // NOTE: Do they want us to try to dispose of their object upon
            //       failure AND are we actually allowed to do so?
            //
            if (dispose &&
                !FlagOps.HasFlags(objectFlags, ObjectFlags.NoDispose, true))
            {
                //
                // NOTE: We failed to add the object to the interpreter, try
                //       to dispose the object since it will otherwise be lost.
                //
                ReturnCode disposeCode;
                Result disposeError = null;

                disposeCode = ObjectOps.TryDispose(
                    value, dispose, ref disposeError);

                if (disposeCode != ReturnCode.Ok)
                    DebugOps.Complain(interpreter, disposeCode, disposeError);
            }

            goto done;

        done:
            return code;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode FixupByRefArray(
            Interpreter interpreter,               /* in */
            IBinder binder,                        /* in */
            CultureInfo cultureInfo,               /* in */
            ObjectFlags objectFlags,               /* in */
            OptionDictionary options,              /* in */
            ObjectOptionType objectOptionType,     /* in */
            string interpName,                     /* in */
            Array array,                           /* in */
            string name,                           /* in */
            ByRefArgumentFlags byRefArgumentFlags, /* in */
            bool create,                           /* in */
            bool dispose,                          /* in */
            bool alias,                            /* in */
            bool aliasReference,                   /* in */
            bool toString,                         /* in */
            ref Result error                       /* out */
            )
        {
            if (interpreter != null)
            {
                int rank;

                if ((array != null) && ((rank = array.Rank) > 0))
                {
                    //
                    // NOTE: In "fast" mode, skip all the extra variable processing.
                    //
                    bool fast = FlagOps.HasFlags(
                        byRefArgumentFlags, ByRefArgumentFlags.Fast, true);

                    //
                    // NOTE: In "direct" mode, bypass all use of SetVariableValue
                    //       (primarily for speed reasons).
                    //
                    bool direct = FlagOps.HasFlags(
                        byRefArgumentFlags, ByRefArgumentFlags.Direct, true);

                    VariableFlags variableFlags = VariableFlags.None;

                    if (fast)
                        variableFlags |= VariableFlags.FastNonInstanceMask;

                    lock (interpreter.SyncRoot) /* TRANSACTIONAL */
                    {
                        //
                        // NOTE: For direct mode, allocate the new array value now.
                        //
                        IVariable variable = null;
                        ElementDictionary arrayValue = null;

                        if (direct)
                        {
                            //
                            // NOTE: Forbid the use of virtual variables in direct
                            //       mode.
                            //
                            variableFlags |= VariableFlags.NonVirtual;

                            if (interpreter.GetVariableViaResolversWithSplit(
                                    name, ref variableFlags, ref variable,
                                    ref error) == ReturnCode.Ok)
                            {
                                if (EntityOps.IsUndefined(variable) ||
                                    EntityOps.IsArray2(variable))
                                {
                                    arrayValue = variable.ArrayValue;

                                    if (arrayValue == null)
                                    {
                                        arrayValue = new ElementDictionary(
                                            interpreter.VariableEvent);

                                        variable.ArrayValue = arrayValue;
                                    }
                                }
                                else
                                {
                                    error = String.Format(
                                        "\"{0}\" isn't an array",
                                        name);

                                    return ReturnCode.Error;
                                }
                            }
                            else
                            {
                                return ReturnCode.Error;
                            }
                        }

                        //
                        // NOTE: Create space to hold the lower bounds, lengths,
                        //       and indexes for each rank of the array.
                        //
                        int[] lowerBounds = null;
                        int[] lengths = null;
                        int[] indexes = null;

                        //
                        // NOTE: Obtain all the bounds for the array.
                        //
                        ArrayOps.GetBounds(
                            array, ref rank, ref lowerBounds, ref lengths,
                            ref indexes);

                        //
                        // NOTE: Iterate over the array N number of times.  For our
                        //       purposes, the actual value of the loop index does
                        //       not matter as it is not used.
                        //
                        ObjectOptionType savedObjectOptionType = objectOptionType;

                        objectOptionType = ObjectOps.GetByRefOptionType(
                            objectOptionType, byRefArgumentFlags);

                        int length = array.Length;

                        for (int unused = 0; unused < length; unused++)
                        {
                            string index = GenericOps<int>.ListToString(
                                indexes, 0, Index.Invalid, ToStringFlags.None,
                                Characters.Comma.ToString(), null, false);

                            object value = array.GetValue(indexes);

                            if (value != null)
                            {
                                Type elementType = value.GetType();

                                if (IsArrayType(elementType))
                                {
                                    string nestedName = FormatOps.NestedArrayName(name, index);

                                    if (FixupByRefArray(
                                            interpreter, binder, cultureInfo, objectFlags,
                                            options, savedObjectOptionType, interpName, (Array)value,
                                            nestedName, byRefArgumentFlags, create, dispose, alias,
                                            aliasReference, toString, ref error) != ReturnCode.Ok)
                                    {
                                        return ReturnCode.Error;
                                    }

                                    //
                                    // NOTE: Now, we need to update the reference in the parent array
                                    //       (below) to point to the nested array.
                                    //
                                    value = nestedName;
                                }

                                //
                                // NOTE: Translate the raw output argument value to a string result
                                //       (possibly a new opaque object handle).
                                //
                                Result result = null;

                                if (FixupReturnValue(
                                        interpreter, binder, cultureInfo, null, objectFlags,
                                        options, objectOptionType, null, interpName, value,
                                        create, dispose, alias, aliasReference, toString,
                                        ref result) != ReturnCode.Ok)
                                {
                                    return ReturnCode.Error;
                                }

                                //
                                // NOTE: Use direct mode?  If so, completely bypass the use of
                                //       SetVariableValue (primarily for speed reasons).
                                //
                                if (direct && (arrayValue != null))
                                {
                                    //
                                    // NOTE: We want the string value here, not the Result
                                    //       object.
                                    //
                                    arrayValue[index] = result.String;
                                }
                                else
                                {
                                    //
                                    // NOTE: This cannot be guaranteed to succeed:
                                    //
                                    //       1. The variable may be read-only.
                                    //
                                    //       2. If the variable has traces, they may opt to
                                    //          override, cancel, or abort setting of the new
                                    //          value.
                                    //
                                    if (interpreter.SetVariableValue2(
                                            variableFlags, name, index, result, null,
                                            ref error) != ReturnCode.Ok)
                                    {
                                        return ReturnCode.Error;
                                    }
                                }
                            }

                            //
                            // NOTE: Increment the indexes by one element for the next
                            //       time around.
                            //
                            ArrayOps.IncrementIndexes(
                                rank, lowerBounds, lengths, indexes);
                        }

                        //
                        // NOTE: In "direct" mode, mark the variable as defined and
                        //       dirty now.
                        //
                        if (direct)
                        {
                            EntityOps.SetUndefined(variable, false);
                            EntityOps.SignalDirty(variable, null);
                        }
                    }

                    return ReturnCode.Ok;
                }
                else
                {
                    error = "array is invalid or has an invalid rank";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode FixupByRefArguments(
            Interpreter interpreter,               /* in */
            IBinder binder,                        /* in */
            CultureInfo cultureInfo,               /* in */
            ArgumentInfoList argumentInfoList,     /* in */
            ObjectFlags objectFlags,               /* in */
            OptionDictionary options,              /* in */
            ObjectOptionType objectOptionType,     /* in */
            string interpName,                     /* in */
            object[] args,                         /* in */
            ByRefArgumentFlags byrefArgumentFlags, /* in */
            bool strict,                           /* in */
            bool create,                           /* in */
            bool dispose,                          /* in */
            bool alias,                            /* in */
            bool aliasReference,                   /* in */
            bool toString,                         /* in */
            bool arrayAsValue,                     /* in */
            bool arrayAsLink,                      /* in */
            ref Result error                       /* out */
            )
        {
            if (interpreter != null)
            {
                if (argumentInfoList != null)
                {
                    if (args != null)
                    {
                        //
                        // NOTE: In "fast" mode, skip all the extra variable processing.
                        //
                        VariableFlags variableFlags = VariableFlags.None;

                        if (FlagOps.HasFlags(byrefArgumentFlags, ByRefArgumentFlags.Fast, true))
                            variableFlags |= VariableFlags.FastNonInstanceMask;

                        ObjectOptionType savedObjectOptionType = objectOptionType;

                        objectOptionType = ObjectOps.GetByRefOptionType(
                            objectOptionType, byrefArgumentFlags);

                        foreach (ArgumentInfo argumentInfo in argumentInfoList)
                        {
                            int parameterIndex = argumentInfo.Index;
                            string parameterName = argumentInfo.Name;
                            object arg = args[parameterIndex];

                            if (arg != null)
                            {
#if DEBUG && MONO
                                //
                                // HACK: *MONO* Mono does not like the use of nullable types here.
                                //       https://bugzilla.novell.com/show_bug.cgi?id=471259
                                //
                                Type argType = null;

                                try
                                {
                                    argType = arg.GetType();
                                }
                                catch
                                {
                                    // do nothing.
                                }
#else
                                Type argType = arg.GetType();
#endif

                                ///////////////////////////////////////////////////////////////////////

#if DEBUG && MONO
                                Type parameterType = null;

                                try
                                {
                                    //
                                    // BUGBUG: I'm not sure if this will cause a problem in
                                    //         Mono 2.0; however, better safe than sorry.
                                    //
                                    parameterType = argumentInfo.Type; // TODO: Test in Mono 2.0.
                                }
                                catch
                                {
                                    // do nothing.
                                }
#else
                                Type parameterType = argumentInfo.Type;
#endif

                                ///////////////////////////////////////////////////////////////////////

                                //
                                // NOTE: Are we operating in strict type checking mode?
                                //
                                if (strict ||
                                    FlagOps.HasFlags(byrefArgumentFlags, ByRefArgumentFlags.Strict, true))
                                {
                                    //
                                    // NOTE: In strict mode, we check the type of the argument
                                    //       object against the formal parameter type we actually
                                    //       expect for this parameter.
                                    //
                                    if (!IsSameValueType(argType, parameterType) &&
                                        !IsSameReferenceType(argType, parameterType))
                                    {
                                        error = String.Format(
                                            "output parameter {0} type \"{1}\" does not match " +
                                            "expected type \"{2}\"", FormatOps.ArgumentName(
                                            parameterIndex, parameterName), argType, parameterType);

                                        return ReturnCode.Error;
                                    }
                                }

                                ///////////////////////////////////////////////////////////////////////

                                //
                                // NOTE: Check for array types.  Arrays are handled specially.
                                //
                                Array array = null;
                                bool isObject = false;

                                if (IsArrayValue(arg, ref array) &&
                                    ((isObject = IsObjectType(parameterType) ||
                                    IsArrayType(parameterType))))
                                {
                                    if (arrayAsValue || FlagOps.HasFlags(byrefArgumentFlags,
                                            ByRefArgumentFlags.ArrayAsValue, true))
                                    {
                                        goto fixupReturnValue;
                                    }

                                    ///////////////////////////////////////////////////////////////////

                                    if (arrayAsLink || FlagOps.HasFlags(byrefArgumentFlags,
                                            ByRefArgumentFlags.ArrayAsLink, true))
                                    {
                                        if (interpreter.SetVariableSystemArray(
                                                variableFlags, parameterName,
                                                array, ref error) == ReturnCode.Ok)
                                        {
                                            continue;
                                        }
                                        else
                                        {
                                            return ReturnCode.Error;
                                        }
                                    }

                                    ///////////////////////////////////////////////////////////////////

                                    //
                                    // BUGFIX: When dealing with the "System.Object" type only,
                                    //         allow array values to override the pre-existing
                                    //         scalar variable value as long as the existing
                                    //         value within the scalar is logically -OR-
                                    //         physically a null value or an empty string.
                                    //
                                    if (isObject)
                                    {
                                        IVariable variable = null;

                                        if ((interpreter.DoesVariableExist(
                                                VariableFlags.NoElement | VariableFlags.Defined,
                                                parameterName, ref variable) == ReturnCode.Ok) &&
                                            IsScalarWithNullOrEmptyValue(interpreter, variable))
                                        {
                                            if (interpreter.UnsetVariable(
                                                    VariableFlags.None, parameterName,
                                                    ref error) != ReturnCode.Ok)
                                            {
                                                return ReturnCode.Error;
                                            }
                                        }
                                    }

                                    ///////////////////////////////////////////////////////////////////

                                    if (FixupByRefArray(
                                            interpreter, binder, cultureInfo, objectFlags, options,
                                            savedObjectOptionType, interpName, array, parameterName,
                                            byrefArgumentFlags,
                                            create || FlagOps.HasFlags(byrefArgumentFlags,
                                                ByRefArgumentFlags.Create, true),
                                            dispose || FlagOps.HasFlags(byrefArgumentFlags,
                                                ByRefArgumentFlags.Dispose, true),
                                            alias || FlagOps.HasFlags(byrefArgumentFlags,
                                                ByRefArgumentFlags.Alias, true),
                                            aliasReference || FlagOps.HasFlags(byrefArgumentFlags,
                                                ByRefArgumentFlags.AliasReference, true),
                                            toString || FlagOps.HasFlags(byrefArgumentFlags,
                                                ByRefArgumentFlags.ToString, true),
                                            ref error) != ReturnCode.Ok)
                                    {
                                        return ReturnCode.Error;
                                    }

                                    continue;
                                }
                            }

                            ///////////////////////////////////////////////////////////////////////////

                        fixupReturnValue:

                            //
                            // NOTE: Translate the raw output argument value to a string
                            //       result (possibly a new opaque object handle).
                            //
                            Result result = null;

                            if (FixupReturnValue(
                                    interpreter, binder, cultureInfo, null, objectFlags,
                                    options, objectOptionType, null, interpName, arg,
                                    create || FlagOps.HasFlags(byrefArgumentFlags,
                                        ByRefArgumentFlags.Create, true),
                                    dispose || FlagOps.HasFlags(byrefArgumentFlags,
                                        ByRefArgumentFlags.Dispose, true),
                                    alias || FlagOps.HasFlags(byrefArgumentFlags,
                                        ByRefArgumentFlags.Alias, true),
                                    aliasReference || FlagOps.HasFlags(byrefArgumentFlags,
                                        ByRefArgumentFlags.AliasReference, true),
                                    toString || FlagOps.HasFlags(byrefArgumentFlags,
                                        ByRefArgumentFlags.ToString, true),
                                    ref result) != ReturnCode.Ok)
                            {
                                return ReturnCode.Error;
                            }

                            ///////////////////////////////////////////////////////////////////////////

                            //
                            // NOTE: This cannot be guaranteed to succeed:
                            //
                            //       1. The variable may be read-only.
                            //
                            //       2. If the variable has traces, they may opt to
                            //          override, cancel, or abort setting of the new
                            //          value.
                            //
                            if (interpreter.SetVariableValue(
                                    variableFlags, parameterName, result, null,
                                    ref error) != ReturnCode.Ok)
                            {
                                return ReturnCode.Error;
                            }
                        }

                        return ReturnCode.Ok;
                    }
                    else
                    {
                        error = "invalid arguments";
                    }
                }
                else
                {
                    error = "invalid argument info";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode FixupScalar(
            Interpreter interpreter,   /* in */
            IBinder binder,            /* in */
            CultureInfo cultureInfo,   /* in */
            Type type,                 /* in */
            ArgumentInfo argumentInfo, /* in */
            MarshalFlags marshalFlags, /* in */
            bool handle,               /* in */
            bool input,                /* in */
            bool output,               /* in */
            ref object arg,            /* in, out */
            ref Result error           /* out */
            )
        {
            if (interpreter != null)
            {
                if ((type != null) && !IsArrayType(type))
                {
                    if (arg != null)
                    {
                        object newArg = arg;

                        if (newArg is string)
                        {
                            Result value = null;
                            Result localError = null;

                            if (interpreter.GetVariableValue(
                                    VariableFlags.None, (string)newArg, ref value,
                                    ref localError) == ReturnCode.Ok)
                            {
                                /* need String, not Result */
                                newArg = value.String;

                                if (FixupValue(
                                        interpreter, binder, cultureInfo, type,
                                        argumentInfo, marshalFlags, handle, input,
                                        output, ref newArg, ref error) == ReturnCode.Ok)
                                {
                                    arg = newArg;

                                    return ReturnCode.Ok;
                                }
                            }
                            else if (FlagOps.HasFlags(
                                    marshalFlags, MarshalFlags.DefaultValue, true))
                            {
                                //
                                // NOTE: The variable does not exist and we have been
                                //       told by the caller to use the default value
                                //       in that case.
                                //
                                arg = GetDefaultValue(type);

                                return ReturnCode.Ok;
                            }
                            else
                            {
                                error = localError;
                            }
                        }
                        else
                        {
                            error = "variable name references must be of type string";
                        }
                    }
                    else
                    {
                        error = "variable name references cannot be null";
                    }
                }
                else
                {
                    if (type != null)
                        error = String.Format(
                            "type \"{0}\" cannot be an array type",
                            type.FullName);
                    else
                        error = "invalid type";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode FixupArray(
            Interpreter interpreter,   /* in */
            IBinder binder,            /* in */
            CultureInfo cultureInfo,   /* in */
            Type type,                 /* in */
            Type elementType,          /* in */
            IVariable variable,        /* in */
            ArgumentInfo argumentInfo, /* in */
            MarshalFlags marshalFlags, /* in */
            bool handle,               /* in */
            bool input,                /* in */
            bool output,               /* in */
            ref object arg,            /* in, out */
            ref Result error           /* out */
            )
        {
            if (interpreter != null)
            {
                int rank = 0;

                if ((type != null) &&
                    (((variable != null) && IsArrayType(variable, ref rank, ref elementType)) ||
                    ((variable == null) && IsArrayType(type, ref rank, ref elementType))) &&
                    (rank > 0) && (elementType != null))
                {
                    if (input || output)
                    {
                        if (arg != null)
                        {
                            object newArg = arg;

                            if (newArg is string)
                            {
                                //
                                // FIXME: PRI 4: Variable traces will not be fired here because we are
                                //        accessing the array elements via the ArrayValues property and
                                //        not through the GetVariableValue method.
                                //
                                // BUGFIX: Make sure we do not end up with an alias to an array element.
                                //         At this point, there cannot be an array element name (via the
                                //         linked variable).  If there is one, that is always an error.
                                //
                                // BUGFIX: Make sure the variable is actually defined *IF* this is an
                                //         input argument.
                                //
                                VariableFlags variableFlags = GetArrayVariableFlags(input);

                                lock (interpreter.SyncRoot) /* TRANSACTIONAL */
                                {
                                    Result localError = null;

                                    if ((variable != null) || interpreter.GetVariableViaResolversWithSplit(
                                            (string)newArg, ref variableFlags,
                                            ref variable, ref localError) == ReturnCode.Ok)
                                    {
                                        if (EntityOps.IsLink(variable))
                                            variable = EntityOps.FollowLinks(variable, variableFlags);

                                        //
                                        // BUGFIX: If the variable is currently undefined AND we are going to
                                        //         allow it to be used anyhow (i.e. because it is output only),
                                        //         then make sure it has a valid value [if necessary].
                                        //
                                        if (!EntityOps.IsArray2(variable))
                                        {
                                            //
                                            // BUGFIX: Try to interpret the value of the variable as a well-known
                                            //         opaque object handle (which can only be a string). If the
                                            //         interpreter is null then the this step will have no effect.
                                            //         This should only be done if the handle has not already been
                                            //         looked up for this value.  If the resulting object is null,
                                            //         allow the variable name to be used.  Otherwise, forbid the
                                            //         variable name from being used and issue an appropriate
                                            //         error message.
                                            //
                                            object value = variable.Value;

                                            if (handle && (value is string) &&
                                                (ArgumentInfo.QueryCount(argumentInfo, 0) <= 0))
                                            {
                                                if (Value.GetObject(interpreter, (string)value,
                                                        ref value) == ReturnCode.Ok)
                                                {
                                                    //
                                                    // NOTE: We have now successfully looked up this object
                                                    //       handle.  Keep track of this fact so this task
                                                    //       is not repeated.
                                                    //
                                                    /* IGNORED */
                                                    ArgumentInfo.IncrementCount(argumentInfo, 0);

                                                    if (value == null)
                                                    {
                                                        //
                                                        // NOTE: *SPECIAL* The array itself is allowed to be
                                                        //       null for both input and output arguments.
                                                        //       Just return now.  Also, if this is an output
                                                        //       parameter, convert the variable into an array
                                                        //       now so that it can be populated later.
                                                        //
                                                        if (output && !FlagOps.HasFlags(marshalFlags,
                                                                MarshalFlags.SkipNullSetupValue, true))
                                                        {
                                                            variable.SetupValue(null, true, true, true, true);
                                                        }

                                                        arg = value;

                                                        return ReturnCode.Ok;
                                                    }
                                                    else if (!output)
                                                    {
                                                        if (IsSameReferenceType(value.GetType(), type))
                                                        {
                                                            //
                                                            // NOTE: *SPECIAL* If the argument is input only,
                                                            //       allow the opaque object handle to refer to
                                                            //       an array of the proper type and just use it
                                                            //       verbatim without any additional processing.
                                                            //
                                                            arg = value;

                                                            return ReturnCode.Ok;
                                                        }
                                                        else
                                                        {
                                                            error = String.Format(
                                                                "cannot convert from type \"{0}\" to type \"{1}\"",
                                                                value.GetType().FullName, type.FullName);

                                                            return ReturnCode.Error;
                                                        }
                                                    }
                                                    else
                                                    {
                                                        error = String.Format(
                                                            "cannot convert scalar variable containing object " +
                                                            "of type \"{0}\" to output array of type \"{1}\"",
                                                            value.GetType().FullName, type.FullName);

                                                        return ReturnCode.Error;
                                                    }
                                                }
                                            }
                                        }

                                        //
                                        // BUGFIX: Make sure the variable is actually defined *IF* this is an
                                        //         input argument.
                                        //
                                        if (EntityOps.IsArray(variable))
                                        {
                                            //
                                            // BUGFIX: If the variable is currently undefined AND we are going
                                            //         to allow it to be used anyhow (i.e. because it is output
                                            //         only), then make sure it has a valid value [if necessary].
                                            //
                                            if (EntityOps.IsUndefined(variable))
                                            {
                                                variable.SetupValue(null, true, true, true, false);
                                            }
                                            //
                                            // HACK: Unless forbidden from doing so, attempt to automatically
                                            //       detect and use an existing System.Array value within the
                                            //       linked variable.
                                            //
                                            else if (!FlagOps.HasFlags(
                                                    marshalFlags, MarshalFlags.NoSystemArray, true) &&
                                                interpreter.IsSystemArrayVariable(variable))
                                            {
                                                arg = EntityOps.GetSystemArray(variable);

                                                return ReturnCode.Ok;
                                            }

                                            int?[] lowerBounds = new int?[rank]; // NOTE: Populated in first pass.
                                            int?[] lengths = new int?[rank]; // NOTE: Populated in first pass.
                                            Array array = null; // NOTE: Created and populated in second pass.

                                            //
                                            // NOTE: The .NET Framework forces us to make multiple passes here
                                            //       because it lacks support for sparse arrays (specifically,
                                            //       we must know the "lengths" of each rank prior to creating
                                            //       or populating the array); therefore, to share the somewhat
                                            //       verbose index parsing and processing code, we artificially
                                            //       divide the work into two passes and identify the work to be
                                            //       done in the body based on the current pass.
                                            //
                                            // BUGFIX: Skip second pass for output-only parameters.
                                            //
                                            for (int pass = 0; pass < (input ? 2 : 1); pass++)
                                            {
                                                //
                                                // NOTE: We must examine and process each element of the script
                                                //       array variable to determine the maximum lengths of each
                                                //       rank (in the first pass) and the actual values to place
                                                //       into the managed array (in the second pass).
                                                //
                                                ElementDictionary arrayValue = variable.ArrayValue;

                                                foreach (string index in arrayValue.Keys)
                                                {
                                                    //
                                                    // NOTE: Ignore index expressions that are null or empty so
                                                    //       that scripts may place out-of-band data about the
                                                    //       array there without causing an error.
                                                    //
                                                    if (!String.IsNullOrEmpty(index))
                                                    {
                                                        //
                                                        // FIXME: Currently, we hard-code this to look for comma
                                                        //        separated indexes.  This is low-priority because
                                                        //        this is considered to be the de-facto standard
                                                        //        for "multi-dimensional" script array indexing.
                                                        //
                                                        string[] subIndexes = index.Split(Characters.Comma);

                                                        if (subIndexes != null)
                                                        {
                                                            int subIndexesLength = subIndexes.Length;

                                                            if (subIndexesLength == rank)
                                                            {
                                                                int[] indexes = new int[rank];

                                                                for (int rankIndex = 0; rankIndex < rank; rankIndex++)
                                                                {
                                                                    if (Value.GetInteger2(
                                                                            subIndexes[rankIndex], ValueFlags.AnyInteger,
                                                                            cultureInfo, ref indexes[rankIndex],
                                                                            ref error) == ReturnCode.Ok)
                                                                    {
                                                                        //
                                                                        // NOTE: Setup lower bounds and lengths
                                                                        //       on first pass only.
                                                                        //
                                                                        if (pass == 0)
                                                                        {
                                                                            int rankLowerBound = indexes[rankIndex];

                                                                            if ((lowerBounds[rankIndex] == null) ||
                                                                                (rankLowerBound < lowerBounds[rankIndex]))
                                                                            {
                                                                                lowerBounds[rankIndex] = rankLowerBound;
                                                                            }

                                                                            int rankLength = indexes[rankIndex] -
                                                                                (int)lowerBounds[rankIndex] + 1;

                                                                            if ((lengths[rankIndex] == null) ||
                                                                                (rankLength > lengths[rankIndex]))
                                                                            {
                                                                                lengths[rankIndex] = rankLength;
                                                                            }
                                                                        }
                                                                    }
                                                                    else
                                                                    {
                                                                        return ReturnCode.Error;
                                                                    }
                                                                }

                                                                if (pass == 1)
                                                                {
                                                                    object value = arrayValue[index];

                                                                    if (elementType.IsArray)
                                                                    {
                                                                        Type subElementType = elementType.GetElementType();

                                                                        if (FixupArray(
                                                                                interpreter, binder, cultureInfo, elementType,
                                                                                subElementType, null, argumentInfo, marshalFlags,
                                                                                handle, input, output, ref value,
                                                                                ref error) != ReturnCode.Ok)
                                                                        {
                                                                            return ReturnCode.Error;
                                                                        }
                                                                    }
                                                                    else
                                                                    {
                                                                        /* IGNORED */
                                                                        ArgumentInfo.ResetCount(argumentInfo, 0);

                                                                        if (FixupValue(
                                                                                interpreter, binder, cultureInfo, elementType,
                                                                                argumentInfo, marshalFlags, handle, input, output,
                                                                                ref value, ref error) != ReturnCode.Ok)
                                                                        {
                                                                            return ReturnCode.Error;
                                                                        }
                                                                    }

#if DEBUG && MONO
                                                                    //
                                                                    // FIXME: Remove this when Mono fixes this bug.
                                                                    //
                                                                    IntList indexList = new IntList(indexes);

                                                                    TraceOps.DebugTrace(String.Format(
                                                                        "{0}: FixupArray: index list is \"{1}\"{2}",
                                                                        GlobalState.GetCurrentThreadId(), indexList,
                                                                        Environment.NewLine), typeof(MarshalOps).Name);
#endif

                                                                    //
                                                                    // NOTE: Finally, set the array element.
                                                                    //
                                                                    array.SetValue(value, indexes);
                                                                }
                                                            }
                                                            else
                                                            {
                                                                error = String.Format(
                                                                    "array index \"{0}\" appears to be of rank {1} " +
                                                                    "and array is of rank {2}",
                                                                    index, subIndexesLength, rank);

                                                                return ReturnCode.Error;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            error = String.Format(
                                                                "could not parse array index \"{0}\"",
                                                                index);

                                                            return ReturnCode.Error;
                                                        }
                                                    }
                                                }

                                                //
                                                // NOTE: After the first pass is complete, validate the lengths
                                                //       and create the array (now that we know the final lengths).
                                                //
                                                if (pass == 0)
                                                {
                                                    for (int rankIndex = 0; rankIndex < rank; rankIndex++)
                                                    {
                                                        if (lengths[rankIndex] < 0)
                                                        {
                                                            error = String.Format(
                                                                "invalid length {0} for array rank {1}",
                                                                lengths[rankIndex], rankIndex + 1);

                                                            return ReturnCode.Error;
                                                        }
                                                    }

                                                    array = Array.CreateInstance(
                                                        GetArrayElementType(elementType),
                                                        ArrayOps.ToNonNullable<int>(lengths, 0),
                                                        ArrayOps.ToNonNullable<int>(lowerBounds, 0));
                                                }
                                            }

                                            //
                                            // NOTE: If we get here, the array should be fully populated.
                                            //
                                            arg = array;

                                            return ReturnCode.Ok;
                                        }
                                        else
                                        {
                                            error = String.Format(
                                                "can't read {0}: variable isn't array",
                                                FormatOps.ErrorVariableName(
                                                    variable, null, (string)newArg, null));
                                        }
                                    }
                                    //
                                    // NOTE: *SPECIAL CASE* Convert a string to an one-dimensional
                                    //       array of characters?
                                    //
                                    else if (FlagOps.HasFlags(variableFlags, VariableFlags.NotFound, true) &&
                                        (rank == 1) &&
                                        (elementType == typeof(char)))
                                    {
                                        arg = ((string)newArg).ToCharArray();

                                        return ReturnCode.Ok;
                                    }
                                    else
                                    {
                                        error = localError;
                                    }
                                }
                            }
                            //
                            // NOTE: *SPECIAL CASE* Convert a list of strings into a one-dimensional
                            //       array of the given type.
                            //
                            else if (newArg is StringList)
                            {
                                if (rank == 1)
                                {
                                    StringList list = (StringList)newArg;

                                    Array array = Array.CreateInstance(
                                        GetArrayElementType(elementType),
                                        list.Count);

                                    for (int index = 0; index < list.Count; index++)
                                    {
                                        object value = list[index];

                                        if (elementType.IsArray)
                                        {
                                            Type subElementType = elementType.GetElementType();

                                            if (FixupArray(
                                                    interpreter, binder, cultureInfo, elementType,
                                                    subElementType, null, argumentInfo, marshalFlags,
                                                    handle, input, output, ref value,
                                                    ref error) != ReturnCode.Ok)
                                            {
                                                return ReturnCode.Error;
                                            }
                                        }
                                        else
                                        {
                                            /* IGNORED */
                                            ArgumentInfo.ResetCount(argumentInfo, 0);

                                            if (FixupValue(
                                                    interpreter, binder, cultureInfo, elementType,
                                                    argumentInfo, marshalFlags, handle, input, output,
                                                    ref value, ref error) != ReturnCode.Ok)
                                            {
                                                return ReturnCode.Error;
                                            }
                                        }

                                        //
                                        // NOTE: We were able to convert the element to the requested
                                        //       type.  Place it in the array at the same index.
                                        //
                                        array.SetValue(value, index);
                                    }

                                    //
                                    // NOTE: If we get here, the array should be fully populated.
                                    //
                                    arg = array;

                                    return ReturnCode.Ok;
                                }
                                else
                                {
                                    error = String.Format(
                                        "cannot convert list to an array of rank {0}",
                                        rank);
                                }
                            }
                            else
                            {
                                error = "variable name references must be of type string";
                            }
                        }
                        else
                        {
                            //
                            // NOTE: Arrays arguments are allowed to be null.
                            //
                            return ReturnCode.Ok;
                        }
                    }
                    else
                    {
                        error = "array must be input and/or output";
                    }
                }
                else
                {
                    if (type != null)
                        error = String.Format(
                            "type \"{0}\" is not an array type or has an invalid rank",
                            type.FullName);
                    else
                        error = "invalid type";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

#if DATA
        public static string FixupDataValue(
            object value,
            DateTimeBehavior dateTimeBehavior,
            string dateTimeFormat,
            string @default
            )
        {
            if (value is string)
            {
                return (string)value;
            }
            else if (value is byte[])
            {
                return new ByteList((byte[])value).ToString();
            }
            else if (value is DateTime)
            {
                switch (dateTimeBehavior)
                {
                    case DateTimeBehavior.Ticks:
                        {
                            return ((DateTime)value).Ticks.ToString();
                        }
                    case DateTimeBehavior.Seconds:
                        {
                            long seconds = 0;

                            if (TimeOps.DateTimeToSeconds(
                                    ref seconds, (DateTime)value,
                                    TimeOps.UnixEpoch))
                            {
                                return seconds.ToString();
                            }
                            else
                            {
                                TraceOps.DebugTrace(String.Format(
                                    "{0}: FixupDataValue: failed to get " +
                                    "seconds from the epoch {1} to {2}{3}",
                                    GlobalState.GetCurrentThreadId(),
                                    TimeOps.UnixEpoch, (DateTime)value,
                                    Environment.NewLine),
                                    typeof(MarshalOps).Name);
                            }
                            break;
                        }
                    case DateTimeBehavior.ToString:
                        {
                            return (dateTimeFormat != null) ?
                                ((DateTime)value).ToString(dateTimeFormat) :
                                value.ToString();
                        }
                }
            }
            else if (value != null)
            {
                return value.ToString();
            }

            return @default;
        }
#endif

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode FixupValue(
            Interpreter interpreter,   /* in */
            IBinder binder,            /* in */
            CultureInfo cultureInfo,   /* in */
            Type type,                 /* in */
            ArgumentInfo argumentInfo, /* in */
            MarshalFlags marshalFlags, /* in */
            bool handle,               /* in */
            bool input,                /* in */
            bool output,               /* in */
            ref object arg,            /* in, out */
            ref Result error           /* out */
            )
        {
            if (interpreter != null)
            {
                if ((type != null) && !IsArrayType(type))
                {
                    if (arg != null)
                    {
                        object newArg = arg;

                        //
                        // NOTE: First, try to interpret the string as a well-known
                        //       opaque object handle (which can only be a string).
                        //       If the interpreter is null then the this step will
                        //       have no effect.  This should only be done if the
                        //       handle has not already been looked up for this
                        //       value.
                        //
                        if (handle && (newArg is string) &&
                            (ArgumentInfo.QueryCount(argumentInfo, 0) <= 0))
                        {
                            if (Value.GetObject(interpreter, (string)newArg,
                                    ref newArg) == ReturnCode.Ok)
                            {
                                //
                                // NOTE: We have now successfully looked up this
                                //       object handle.  Keep track of this fact
                                //       so this task is not repeated.
                                //
                                /* IGNORED */
                                ArgumentInfo.IncrementCount(argumentInfo, 0);
                            }
                        }

                        //
                        // NOTE: Next, try to interpet our [possibly new] argument
                        //       string as one of the "primitive" types that can
                        //       parse and understand, in ascending order of size.
                        //
                        if (newArg != null)
                        {
                            if (IsValueType(type, output))
                            {
                                //
                                // NOTE: Special case for value types that are baked
                                //       into an object reference, we do not want to
                                //       attempt any further unnecessary conversions
                                //       on them (e.g. System.IntPtr, etc).  Also, we
                                //       explicitly allow nullable types here even
                                //       though in this case it does not matter since
                                //       we know the value is not null.
                                //
                                if (IsSameValueType(newArg.GetType(), type, output))
                                {
                                    arg = newArg;

                                    return ReturnCode.Ok;
                                }
                                else
                                {
                                    if (binder != null)
                                    {
                                        //
                                        // NOTE: Ok, it does not appear to be convertible to one of our
                                        //       built-in value types; however, it may still be valid.
                                        //       Here, we just pass it off to the ScriptBinder.
                                        //
                                        try
                                        {
                                            //
                                            // NOTE: First, try to use their selected binder to convert the
                                            //       value.
                                            //
                                            // HACK: Currently, this basically assumes that their
                                            //       "selected binder" is really the ScriptBinder that can
                                            //       only actually convert values from strings to their
                                            //       appropriate type.
                                            //
                                            // NOTE: In the future, we may not want to unconditionally call
                                            //       ToString here on the candidate value, especially for
                                            //       reference types.  In that case, the ScriptBinder would
                                            //       need to be adapted to handle conversions that are not
                                            //       always from a string.
                                            //
                                            arg = binder.ChangeType(newArg.ToString(), type, cultureInfo);

                                            //
                                            // HACK: If the selected binder did not actually manage to
                                            //       convert the value to the appropriate type and it did
                                            //       not throw an exception (i.e. the ScriptBinder), throw
                                            //       one on its behalf.
                                            //
                                            if ((arg == null) ||
                                                !IsSameValueType(arg.GetType(), type, output))
                                            {
                                                throw new ScriptException(String.Format(
                                                    "value type mismatch, type \"{0}\" is not compatible " +
                                                    "with type \"{1}\"",
                                                    arg.GetType(), type));
                                            }

                                            return ReturnCode.Ok;
                                        }
                                        catch
                                        {
                                            // do nothing.
                                        }
                                    }

                                    //
                                    // HACK: With the new modifications to the ScriptBinder to handle
                                    //       reference and nullable types, we should no longer have
                                    //       any real reasons to reach this point; however, we can
                                    //       abuse the built-in ChangeType to generate an exception
                                    //       explaining why the value cannot be converted.
                                    //
                                    try
                                    {
                                        //
                                        // NOTE: Next, fallback to the default handling of using the
                                        //       built-in ChangeType (typically used for primitive
                                        //       value conversions or trivial reference conversions
                                        //       only).
                                        //
                                        arg = Convert.ChangeType(newArg, type);

                                        return ReturnCode.Ok;
                                    }
                                    catch (Exception e)
                                    {
                                        error = e;
                                    }
                                }
                            }
                            else if (IsSameReferenceType(newArg.GetType(), type, output))
                            {
                                arg = newArg;

                                return ReturnCode.Ok;
                            }
                            else
                            {
                                //
                                // NOTE: Don't bother trying to do anything else at this point if
                                //       the type to convert to is string because we implicitly
                                //       "convert" everything to string prior to passing it into
                                //       the selected binder (i.e. the ScriptBinder) as it only
                                //       "knows" how to convert to other types starting from a
                                //       string.
                                //
                                if ((binder != null) && (type != typeof(string)) &&
                                    (FlagOps.HasFlags(marshalFlags, MarshalFlags.StringList, true) ||
                                    !IsStringListForChangeType(binder, type)))
                                {
                                    try
                                    {
                                        //
                                        // NOTE: First, try to use their selected binder to convert the
                                        //       value to whatever this reference type is.
                                        //
                                        // HACK: Currently, this basically assumes that their
                                        //       "selected binder" is really the ScriptBinder that can
                                        //       only actually convert values from strings to their
                                        //       appropriate type.
                                        //
                                        // NOTE: In the future, we may not want to unconditionally call
                                        //       ToString here on the candidate value, especially for
                                        //       reference types.  In that case, the ScriptBinder would
                                        //       need to be adapted to handle conversions that are not
                                        //       always from a string.
                                        //
                                        arg = binder.ChangeType(newArg.ToString(), type, cultureInfo);

                                        //
                                        // HACK: If the selected binder did not actually manage to
                                        //       convert the value to the appropriate type and it did
                                        //       not throw an exception (i.e. the ScriptBinder), throw
                                        //       one on its behalf.
                                        //
                                        if ((arg == null) ||
                                            !IsSameReferenceType(arg.GetType(), type, output))
                                        {
                                            throw new ScriptException(String.Format(
                                                "reference type mismatch, type \"{0}\" is not compatible " +
                                                "with type \"{1}\"",
                                                arg.GetType(), type));
                                        }

                                        return ReturnCode.Ok;
                                    }
                                    catch (Exception e)
                                    {
                                        error = e;
                                    }
                                }
                                else
                                {
                                    error = String.Format(
                                        "cannot convert from type \"{0}\" to type \"{1}\"",
                                        newArg.GetType().FullName, type.FullName);
                                }
                            }
                        }
                        else
                        {
                            if (IsValueType(type) && !IsNullableType(type, output))
                            {
                                error = String.Format(
                                    "argument of value type \"{0}\" cannot be null",
                                    type.FullName);
                            }
                            else
                            {
                                arg = newArg;

                                return ReturnCode.Ok;
                            }
                        }
                    }
                    else
                    {
                        if (IsValueType(type) && !IsNullableType(type, output))
                        {
                            error = String.Format(
                                "argument of value type \"{0}\" cannot be null",
                                type.FullName);
                        }
                        else
                        {
                            return ReturnCode.Ok;
                        }
                    }
                }
                else
                {
                    if (type != null)
                        error = String.Format(
                            "type \"{0}\" cannot be an array type",
                            type.FullName);
                    else
                        error = "invalid type";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode FixupArgument(
            Interpreter interpreter,   /* in */
            IBinder binder,            /* in */
            CultureInfo cultureInfo,   /* in */
            Type type,                 /* in */
            ArgumentInfo argumentInfo, /* in */
            MarshalFlags marshalFlags, /* in */
            bool handle,               /* in */
            bool input,                /* in */
            bool output,               /* in */
            ref object arg,            /* in, out */
            ref Result error           /* out */
            )
        {
            if (interpreter != null)
            {
                Type elementType = null;

                if ((type != null) &&
                    (!IsArrayType(type, ref elementType) || (elementType != null)))
                {
                    if (input || output)
                    {
                        if (arg != null)
                        {
                            object newArg = arg;

                            //
                            // NOTE: First, try to interpret the string as a well-known
                            //       opaque object handle (which can only be a string).
                            //       If the interpreter is null then the this step will
                            //       have no effect.  This should only be done if the
                            //       handle has not already been looked up for this
                            //       value.
                            //
                            if (handle && (newArg is string) &&
                                (ArgumentInfo.QueryCount(argumentInfo, 0) <= 0))
                            {
                                if (Value.GetObject(interpreter, (string)newArg,
                                        ref newArg) == ReturnCode.Ok)
                                {
                                    //
                                    // NOTE: We have now successfully looked up this
                                    //       object handle.  Keep track of this fact
                                    //       so this task is not repeated.
                                    //
                                    /* IGNORED */
                                    ArgumentInfo.IncrementCount(argumentInfo, 0);
                                }
                            }

                            //
                            // NOTE: Next, try to interpet our [possibly new] argument
                            //       string as one of the "primitive" types that can
                            //       parse and understand, in ascending order of size.
                            //
                            if (newArg != null)
                            {
                                if (output)
                                {
                                    //
                                    // NOTE: The argument value must be a script variable name;
                                    //       therefore, it must be a string.
                                    //
                                    if (newArg is string)
                                    {
                                        //
                                        // NOTE: If the argument is used for input and output (ref),
                                        //       we need to make sure the argument refers to an existing
                                        //       script variable.
                                        //
                                        if (input)
                                        {
                                            //
                                            // NOTE: Argument is input-output.
                                            //
                                            if (IsArrayType(type))
                                            {
                                                //
                                                // NOTE: The variable name must refer to an existing script
                                                //       array variable.
                                                //
                                                if (FixupArray(
                                                        interpreter, binder, cultureInfo, type, elementType,
                                                        null, argumentInfo, marshalFlags, handle, input,
                                                        output, ref newArg, ref error) == ReturnCode.Ok)
                                                {
                                                    arg = newArg;

                                                    return ReturnCode.Ok;
                                                }
                                            }
                                            else
                                            {
                                                IVariable variable = null;

                                                if (IsObjectType(type) && IsArray(
                                                        interpreter, GetArrayVariableFlags(input),
                                                        (string)newArg, ref variable))
                                                {
                                                    //
                                                    // NOTE: The variable name must refer to an existing script
                                                    //       array variable.
                                                    //
                                                    if (FixupArray(
                                                            interpreter, binder, cultureInfo, type, elementType,
                                                            variable, argumentInfo, marshalFlags, handle, input,
                                                            output, ref newArg, ref error) == ReturnCode.Ok)
                                                    {
                                                        arg = newArg;

                                                        return ReturnCode.Ok;
                                                    }
                                                }
                                                else
                                                {
                                                    //
                                                    // NOTE: The variable name must refer to an existing script
                                                    //       scalar variable.
                                                    //
                                                    if (FixupScalar(
                                                            interpreter, binder, cultureInfo, type,
                                                            argumentInfo, marshalFlags, handle, input,
                                                            output, ref newArg, ref error) == ReturnCode.Ok)
                                                    {
                                                        arg = newArg;

                                                        return ReturnCode.Ok;
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            //
                                            // NOTE: Argument is output-only.
                                            //
                                            if (IsArrayType(type))
                                            {
                                                //
                                                // NOTE: If the variable name exists, it must not refer to
                                                //       a script scalar variable.
                                                //
                                                if (interpreter.DoesVariableExist(VariableFlags.NoElement,
                                                        (string)newArg) == ReturnCode.Ok)
                                                {
                                                    if (FixupArray(
                                                            interpreter, binder, cultureInfo,
                                                            type, elementType, null, argumentInfo,
                                                            marshalFlags, handle, input, output,
                                                            ref newArg, ref error) == ReturnCode.Ok)
                                                    {
                                                        arg = newArg;

                                                        return ReturnCode.Ok;
                                                    }
                                                }
                                                else
                                                {
                                                    //
                                                    // NOTE: The variable does not exist.  This is allowed
                                                    //       because it is output-only.  We simply clear out
                                                    //       the argument value and return.
                                                    //
                                                    arg = GetDefaultValue(type);

                                                    return ReturnCode.Ok;
                                                }
                                            }
                                            else
                                            {
                                                //
                                                // NOTE: If the variable name exists, it must not refer to
                                                //       a script array variable.
                                                //
                                                if (interpreter.DoesVariableExist(VariableFlags.None,
                                                        (string)newArg) == ReturnCode.Ok)
                                                {
                                                    if (FixupScalar(
                                                            interpreter, binder, cultureInfo, type,
                                                            argumentInfo, marshalFlags, handle, input,
                                                            output, ref newArg, ref error) == ReturnCode.Ok)
                                                    {
                                                        arg = newArg;

                                                        return ReturnCode.Ok;
                                                    }
                                                }
                                                else
                                                {
                                                    //
                                                    // NOTE: The variable does not exist.  This is allowed
                                                    //       because it is output-only.  We simply clear out
                                                    //       the argument value and return.
                                                    //
                                                    arg = GetDefaultValue(type);

                                                    return ReturnCode.Ok;
                                                }
                                            }
                                        }
                                    }
                                    else if (IsSameReferenceType(newArg.GetType(), type, output))
                                    {
                                        //
                                        // NOTE: The argument is already of the correct [array] type.
                                        //
                                        arg = newArg;

                                        return ReturnCode.Ok;
                                    }
                                    else
                                    {
                                        //
                                        // NOTE: This is an error because output arguments must refer
                                        //       to a script variable by name (which is always a string).
                                        //
                                        error = "variable name references must be of type string";
                                    }
                                }
                                else
                                {
                                    //
                                    // NOTE: Argument is input-only.
                                    //
                                    if (IsArrayType(type))
                                    {
                                        //
                                        // NOTE: We do not support passing arrays by value; therefore,
                                        //       this argument value must be an existing variable name
                                        //       that refers to a script array variable with elements
                                        //       that can be coerced to the appropriate type.
                                        //
                                        if (newArg is string)
                                        {
                                            //
                                            // BUGFIX: *SPECIAL* When resolving member arguments, we do
                                            //         NOT want to try to interpret the argument value
                                            //         as an array variable name is they provided us an
                                            //         actual string and the destination type is a
                                            //         character array.
                                            //
                                            if (type == typeof(char[]))
                                            {
                                                arg = newArg.ToString().ToCharArray();

                                                return ReturnCode.Ok;
                                            }
                                            else
                                            {
                                                //
                                                // NOTE: If the variable name exists, it must not refer to
                                                //       a script scalar variable.
                                                //
                                                if (interpreter.DoesVariableExist(VariableFlags.NoElement,
                                                        (string)newArg) == ReturnCode.Ok)
                                                {
                                                    if (FixupArray(
                                                            interpreter, binder, cultureInfo,
                                                            type, elementType, null, argumentInfo,
                                                            marshalFlags, handle, input, output,
                                                            ref newArg, ref error) == ReturnCode.Ok)
                                                    {
                                                        arg = newArg;

                                                        return ReturnCode.Ok;
                                                    }
                                                }
                                                else
                                                {
                                                    StringList list = null;

                                                    if (Parser.SplitList(
                                                            interpreter, (string)newArg, 0,
                                                            Length.Invalid, true, ref list,
                                                            ref error) == ReturnCode.Ok)
                                                    {
                                                        newArg = list;

                                                        if (FixupArray(
                                                                interpreter, binder, cultureInfo,
                                                                type, elementType, null, argumentInfo,
                                                                marshalFlags, handle, input, output,
                                                                ref newArg, ref error) == ReturnCode.Ok)
                                                        {
                                                            arg = newArg;

                                                            return ReturnCode.Ok;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        else if (IsSameReferenceType(newArg.GetType(), type, output))
                                        {
                                            //
                                            // NOTE: The argument is already of the correct [array] type.
                                            //
                                            arg = newArg;

                                            return ReturnCode.Ok;
                                        }
                                        else
                                        {
                                            //
                                            // NOTE: This is an error because output arguments must refer
                                            //       to a script variable by name (which is always a string).
                                            //
                                            error = "variable name references must be of type string";
                                        }
                                    }
                                    else
                                    {
                                        //
                                        // NOTE: Argument is input-only, non-array.
                                        //
                                        if (FixupValue(
                                                interpreter, binder, cultureInfo, type,
                                                argumentInfo, marshalFlags, handle, input,
                                                output, ref newArg, ref error) == ReturnCode.Ok)
                                        {
                                            arg = newArg;

                                            return ReturnCode.Ok;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (IsValueType(type) && !IsNullableType(type, output))
                                {
                                    error = String.Format(
                                        "argument of value type \"{0}\" cannot be null",
                                        type.FullName);
                                }
                                else
                                {
                                    arg = newArg;

                                    return ReturnCode.Ok;
                                }
                            }
                        }
                        else
                        {
                            if (IsValueType(type) && !IsNullableType(type, output))
                            {
                                error = String.Format(
                                    "argument of value type \"{0}\" cannot be null",
                                    type.FullName);
                            }
                            else
                            {
                                return ReturnCode.Ok;
                            }
                        }
                    }
                    else
                    {
                        error = "argument must be input and/or output";
                    }
                }
                else
                {
                    if (type != null)
                        error = String.Format(
                            "type \"{0}\" is an array type with missing element type",
                            type.FullName);
                    else
                        error = "invalid type";
                }
            }
            else
            {
                error = "invalid interpreter";
            }

            return ReturnCode.Error;
        }
    }
}
