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()