public static object Transform()

in DbgProvider/public/Commands/TransformationAttributes.cs [138:417]


        public static object Transform( EngineIntrinsics engineIntrinsics,
                                        string dbgProviderPath,
                                        bool skipGlobalSymbolTest,
                                        bool throwOnFailure,
                                        bool dbgMemoryPassthru,
                                        bool allowList,
                                        object inputData )
        {
            //Console.WriteLine( "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" );
            //Console.WriteLine( "{0} 1: inputData type: {1}", DebuggingTag, inputData.GetType().FullName );
            //Console.WriteLine( "{0} 2: dynamic type: {1}", DebuggingTag, ((dynamic) inputData).GetType().FullName );
            //Console.WriteLine( "{0} 3: ToString(): {1}", DebuggingTag, inputData.ToString() );
            //Console.WriteLine( "{0} 4: dynamic ToString(): {1}", DebuggingTag, ((dynamic) inputData).ToString() );

            var pso = inputData as PSObject;

            if( allowList )
            {
                var objList = inputData as IList;

                if( (null != pso) && (pso.BaseObject is IList) )
                {
                    objList = (IList) pso.BaseObject;
                }

                if( null != objList )
                {
                    ulong[] addrs = new ulong[ objList.Count ];
                    try
                    {
                        for( int i = 0; i < objList.Count; i++ )
                        {
                            addrs[ i ] = (ulong) Transform( engineIntrinsics,
                                                            dbgProviderPath,
                                                            skipGlobalSymbolTest,
                                                            true, // throwOnFailure,
                                                            dbgMemoryPassthru,
                                                            false, // we don't allow nested arrays
                                                            objList[ i ] );
                        } // end for( each obj )
                        return addrs;
                    }
                    catch( Exception e_temp )
                    {
                        if( throwOnFailure ||
                            (!(e_temp is DbgProviderException) && !(e_temp is MetadataException)) )
                        {
                            throw;
                        }
                    }
                } // end if( it's an array )
            } // end if( allowList )

            if( null != pso )
            {
                // Addresses are always expressed in hexadecimal.
                //
                // Thus you can type a leading "0x", but it is redundant and not
                // necessary.
                //
                // If the address contains a backtick, or for some reason is expressed in
                // decimal with a leading "0n", or has hex-only digits ([a-f]) but no
                // leading "0x", then PowerShell will parse it as a string. Then we can
                // handle parsing it ourselves (in this method).
                //
                // However... if the user /did/ use a leading "0x" and there is no
                // backtick, OR if the address contains no hex-only digits and there is no
                // backtick, OR if the address ended with "eN" (where N is a digit)...
                // then PowerShell will have parsed it as a number (giving us either an
                // UInt32, or a UInt64, or a double, depending on how big).
                //
                // In that case, we need to figure out whether or not the user typed an
                // "0x", because if they did not, that means that PowerShell parsed it
                // incorrectly (as a base-10 number, and possibly using scientific
                // notation, instead of a base-16 number).
                //
                // Fortunately, if we have the PSObject for that typed number, we can get
                // the originally typed string, which will let us know if there was an
                // "0x" or not.
                //
                // Update: "if we have the PSObject for that typed number, we can get the
                // originally typed string": unfortunately, that is not always true, such
                // as when the address is piped in. TODO: can we change PowerShell to
                // allow us to get the string as originally typed in more cases?

                //Console.WriteLine( "{0} 5: BaseObject type: {1}", DebuggingTag, pso.BaseObject.GetType().FullName );
                if( (pso.BaseObject is int) ||
                    (pso.BaseObject is long) ||
                    (pso.BaseObject is double) ||
                    (pso.BaseObject is float) )
                {
                    // The standard way to get the originally typed string is to use
                    // LanguagePrimitives.ConvertTo< string >. However, it seems that it
                    // wants to /always/ give us a string back, even if it doesn't have
                    // the originally typed string. So if we use that method, we don't
                    // know if the string we get back actually is what was originally
                    // typed or not.
                    //var asTyped = LanguagePrimitives.ConvertTo< string >( pso );

                    // This /will/ get what the user actually typed (if it was typed),
                    // but relies on reflection to get at PS internals. :(
                    var asTyped = _GetAsTyped_usingIckyPrivateReflection( pso );
                    //Console.WriteLine( "As typed: {0}", asTyped );

                    if( null != asTyped )
                    {
                        if( asTyped.StartsWith( "0x", StringComparison.OrdinalIgnoreCase ) )
                        {
                            // Yes, they typed an "0x", so PS correctly parsed as hex.
                            //
                            // The cast to (int) first is to un-box. Then to (uint) to
                            // prevent sign extension.
                            if( pso.BaseObject is int )
                                return (ulong) (uint) (int) pso.BaseObject;

                            if( pso.BaseObject is long )
                                return unchecked( (ulong) (long) pso.BaseObject );

                            // Should not reach here.
                            Util.Fail( "How could the typed string start with 0x but get parsed as something besides an int or long?" );
                        }

                        inputData = asTyped; // we'll re-parse it below as base-16
                    }
                    else
                    {
                        // If we get here, then it /was/ typed, but piped in:
                        //
                        //    01234000 | ConvertTo-Number
                        //  0x01234000 | ConvertTo-Number
                        //
                        // So PS parsed it... but if we ended up with an integer type, we
                        // don't know if it parsed as decimal or hex, so we can't be sure
                        // how to undo that parsing. :(  For now we'll have to just assume
                        // that the user knows that they need to use 0x when piping in.
                        //
                        // That sounds bad, because actually a user probably will /not/
                        // know that, but the alternative is worse; a user who directly
                        // specifies hex ("0x01230000 | something") should never get the
                        // wrong result.
                        //
                        // TODO: see if we can get PS to preserve the as-typed value for
                        // things that are piped in.
                        inputData = pso.BaseObject;
                    }
                }
                else
                {
                    inputData = pso.BaseObject;
                }
            }
            //Console.WriteLine( "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" );

            if( dbgMemoryPassthru && (inputData is DbgMemory) )
                return inputData;

            // Some commands do not require an address.
            if( null == inputData )
                return (ulong) 0;

            if( inputData is ulong )
                return inputData;

            // We used to assume that this was probably a base-16 number without any
            // letters in it, so PS interpreted it as a base-10 number, so then we would
            // undo that. And hope it was right. But unfortunately, that messed up the
            // scenario where you assign a number to a variable ("$blah = 123"), so I'm
            // going the other way now--we'll assume it's already correct. Unfortunately,
            // that means that on 32-bit, you'll need to always use "0x" to be safe. :(
         // if( (inputData is int) || (inputData is long) )
         // {
         //     inputData = inputData.ToString();
         // }

            if( inputData is int )
                return (ulong) (uint) (int) inputData; // 1st cast unboxes; 2nd prevents sign extension

            if( inputData is long )
                return (ulong) (long) inputData; // need two casts in order to unbox first.

            if( inputData is uint )
            {
                // This can happen, for instance, when using the register variables for a
                // 32-bit process ("u $eip").
                return (ulong) (uint) inputData; // need two casts in order to unbox first.
            }

            if( inputData is byte )
            {
                // This can happen because we [ab]use AddressTransformationAttribute to
                // convert lots of numeric data, not just addresses. (For instance, the
                // "eb" command.)
                return (ulong) (byte) inputData; // need two casts in order to unbox first.
            }

            if( inputData is double )
            {
                // This can happen when doing arithmetic. For instance, this will yield
                // Double:
                //
                //    [UInt32] $ui1 = 0x03
                //    [UInt32] $ui2 = 0x01
                //    ($ui2 - $ui1).GetType()
                //
                // To determine if it's really something that can be represented as a
                // ulong, we'll round-trip it through a ulong back to a double, and then
                // see if the bits representation matches the original double.

                double dOrig = (double) inputData;
                Int64 origBits = BitConverter.DoubleToInt64Bits( dOrig );
                unchecked
                {
                    ulong asUlong = (ulong) (long) dOrig;
                    double d2 = Convert.ToDouble( (long) asUlong );
                    Int64 d2Bits = BitConverter.DoubleToInt64Bits( d2 );
                    if( d2Bits == origBits )
                    {
                        // We round-tripped back to double: it doesn't have any fractional
                        // part.
                        return asUlong;
                    }
                }
            } // end if( inputData is double )

            Exception e = null;
            string str = inputData as string;
            if( null != str )
            {
                // Some commands do not require an address.
                if( 0 == str.Length )
                    return (ulong) 0;

                if( (1 == str.Length) && (str[ 0 ] == '.') )
                {
                    dbgProviderPath = _GetDbgProviderPath( dbgProviderPath, engineIntrinsics );
                    var regSet = DbgProvider.GetRegisterSetForPath( dbgProviderPath );
                    return regSet.Pseudo[ "$ip" ].Value;
                }

                ulong address;
                if( DbgProvider.TryParseHexOrDecimalNumber( str, out address ) )
                    return address;

                // Mabye it's a symbolic name?
                if( !skipGlobalSymbolTest )
                {
                    dbgProviderPath = _GetDbgProviderPath( dbgProviderPath, engineIntrinsics );
                    var debugger = DbgProvider.GetDebugger( dbgProviderPath );
                    try
                    {
                        address = debugger.GetOffsetByName( str );
                        return address;
                    }
                    catch( DbgProviderException dpe )
                    {
                        e = dpe;
                    }
                }
            }

            // Check for implicit conversion to ulong. (For instance, types that derive
            // from DbgPointerValueBase have this.)
            ulong addr;
            if( _TryImplicitConversionTo( inputData, out addr ) )
                return addr;

            if( !throwOnFailure )
                return null;

            if( null != e )
            {
                ExceptionDispatchInfo.Capture( e ).Throw();
            }

            // https://github.com/PowerShell/PowerShell/issues/7600
            //
            // For parameter binding to be able to continue (for example, to try binding
            // by property name), this exception needs to wrap a PSInvalidCastException.
            throw CreateRecoverableAtme( "Could not convert '{0}' to an address.", inputData );
        } // end Transform()