740 lines
No EOL
15 KiB
C#
740 lines
No EOL
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using Emit = System.Reflection.Emit;
|
|
|
|
namespace Server
|
|
{
|
|
public class AssemblyEmitter
|
|
{
|
|
private string m_AssemblyName;
|
|
|
|
private AppDomain m_AppDomain;
|
|
private AssemblyBuilder m_AssemblyBuilder;
|
|
private ModuleBuilder m_ModuleBuilder;
|
|
|
|
public AssemblyEmitter( string assemblyName, bool canSave )
|
|
{
|
|
m_AssemblyName = assemblyName;
|
|
|
|
m_AppDomain = AppDomain.CurrentDomain;
|
|
|
|
m_AssemblyBuilder = m_AppDomain.DefineDynamicAssembly(
|
|
new AssemblyName( assemblyName ),
|
|
canSave ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run
|
|
);
|
|
|
|
if ( canSave )
|
|
{
|
|
m_ModuleBuilder = m_AssemblyBuilder.DefineDynamicModule(
|
|
assemblyName,
|
|
String.Format( "{0}.dll", assemblyName.ToLower() ),
|
|
false
|
|
);
|
|
}
|
|
else
|
|
{
|
|
m_ModuleBuilder = m_AssemblyBuilder.DefineDynamicModule(
|
|
assemblyName,
|
|
false
|
|
);
|
|
}
|
|
}
|
|
|
|
public TypeBuilder DefineType( string typeName, TypeAttributes attrs, Type parentType )
|
|
{
|
|
return m_ModuleBuilder.DefineType( typeName, attrs, parentType );
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
m_AssemblyBuilder.Save(
|
|
String.Format( "{0}.dll", m_AssemblyName.ToLower() )
|
|
);
|
|
}
|
|
}
|
|
|
|
public class MethodEmitter
|
|
{
|
|
private TypeBuilder m_TypeBuilder;
|
|
|
|
private MethodBuilder m_Builder;
|
|
private ILGenerator m_Generator;
|
|
|
|
private Type[] m_ArgumentTypes;
|
|
|
|
public TypeBuilder Type
|
|
{
|
|
get { return m_TypeBuilder; }
|
|
}
|
|
|
|
public ILGenerator Generator
|
|
{
|
|
get { return m_Generator; }
|
|
}
|
|
|
|
private class CallInfo
|
|
{
|
|
public Type type;
|
|
public MethodInfo method;
|
|
|
|
public int index;
|
|
public ParameterInfo[] parms;
|
|
|
|
public CallInfo( Type type, MethodInfo method )
|
|
{
|
|
this.type = type;
|
|
this.method = method;
|
|
|
|
this.parms = method.GetParameters();
|
|
}
|
|
}
|
|
|
|
private Stack<Type> m_Stack;
|
|
private Stack<CallInfo> m_Calls;
|
|
|
|
private Dictionary<Type, Queue<LocalBuilder>> m_Temps;
|
|
|
|
public MethodBuilder Method
|
|
{
|
|
get { return m_Builder; }
|
|
}
|
|
|
|
public MethodEmitter( TypeBuilder typeBuilder )
|
|
{
|
|
m_TypeBuilder = typeBuilder;
|
|
|
|
m_Temps = new Dictionary<Type, Queue<LocalBuilder>>();
|
|
|
|
m_Stack = new Stack<Type>();
|
|
m_Calls = new Stack<CallInfo>();
|
|
}
|
|
|
|
public void Define( string name, MethodAttributes attr, Type returnType, Type[] parms )
|
|
{
|
|
m_Builder = m_TypeBuilder.DefineMethod( name, attr, returnType, parms );
|
|
m_Generator = m_Builder.GetILGenerator();
|
|
|
|
m_ArgumentTypes = parms;
|
|
}
|
|
|
|
public LocalBuilder CreateLocal( Type localType )
|
|
{
|
|
return m_Generator.DeclareLocal( localType );
|
|
}
|
|
|
|
public LocalBuilder AcquireTemp( Type localType )
|
|
{
|
|
Queue<LocalBuilder> list;
|
|
|
|
if ( !m_Temps.TryGetValue( localType, out list ) )
|
|
m_Temps[localType] = list = new Queue<LocalBuilder>();
|
|
|
|
if ( list.Count > 0 )
|
|
return list.Dequeue();
|
|
|
|
return CreateLocal( localType );
|
|
}
|
|
|
|
public void ReleaseTemp( LocalBuilder local )
|
|
{
|
|
Queue<LocalBuilder> list;
|
|
|
|
if ( !m_Temps.TryGetValue( local.LocalType, out list ) )
|
|
m_Temps[local.LocalType] = list = new Queue<LocalBuilder>();
|
|
|
|
list.Enqueue( local );
|
|
}
|
|
|
|
public void Branch( Label label )
|
|
{
|
|
m_Generator.Emit( OpCodes.Br, label );
|
|
}
|
|
|
|
public void BranchIfFalse( Label label )
|
|
{
|
|
Pop( typeof( object ) );
|
|
|
|
m_Generator.Emit( OpCodes.Brfalse, label );
|
|
}
|
|
|
|
public void BranchIfTrue( Label label )
|
|
{
|
|
Pop( typeof( object ) );
|
|
|
|
m_Generator.Emit( OpCodes.Brtrue, label );
|
|
}
|
|
|
|
public Label CreateLabel()
|
|
{
|
|
return m_Generator.DefineLabel();
|
|
}
|
|
|
|
public void MarkLabel( Label label )
|
|
{
|
|
m_Generator.MarkLabel( label );
|
|
}
|
|
|
|
public void Pop()
|
|
{
|
|
m_Stack.Pop();
|
|
}
|
|
|
|
public void Pop( Type expected )
|
|
{
|
|
if ( expected == null )
|
|
throw new InvalidOperationException( "Expected type cannot be null." );
|
|
|
|
Type onStack = m_Stack.Pop();
|
|
|
|
if ( expected == typeof( bool ) )
|
|
expected = typeof( int );
|
|
|
|
if ( onStack == typeof( bool ) )
|
|
onStack = typeof( int );
|
|
|
|
if ( !expected.IsAssignableFrom( onStack ) )
|
|
throw new InvalidOperationException( "Unexpected stack state." );
|
|
}
|
|
|
|
public void Push( Type type )
|
|
{
|
|
m_Stack.Push( type );
|
|
}
|
|
|
|
public void Return()
|
|
{
|
|
if ( m_Stack.Count != ( m_Builder.ReturnType == typeof( void ) ? 0 : 1 ) )
|
|
throw new InvalidOperationException( "Stack return mismatch." );
|
|
|
|
m_Generator.Emit( OpCodes.Ret );
|
|
}
|
|
|
|
public void LoadNull()
|
|
{
|
|
LoadNull( typeof( object ) );
|
|
}
|
|
|
|
public void LoadNull( Type type )
|
|
{
|
|
Push( type );
|
|
|
|
m_Generator.Emit( OpCodes.Ldnull );
|
|
}
|
|
|
|
public void Load( string value )
|
|
{
|
|
Push( typeof( string ) );
|
|
|
|
if ( value != null )
|
|
m_Generator.Emit( OpCodes.Ldstr, value );
|
|
else
|
|
m_Generator.Emit( OpCodes.Ldnull );
|
|
}
|
|
|
|
public void Load( Enum value )
|
|
{
|
|
int toLoad = ((IConvertible)value).ToInt32( null );
|
|
Load( toLoad );
|
|
|
|
Pop();
|
|
Push( value.GetType() );
|
|
}
|
|
|
|
public void Load( long value )
|
|
{
|
|
Push( typeof( long ) );
|
|
|
|
m_Generator.Emit( OpCodes.Ldc_I8, value );
|
|
}
|
|
|
|
public void Load( float value )
|
|
{
|
|
Push( typeof( float ) );
|
|
|
|
m_Generator.Emit( OpCodes.Ldc_R4, value );
|
|
}
|
|
|
|
public void Load( double value )
|
|
{
|
|
Push( typeof( double ) );
|
|
|
|
m_Generator.Emit( OpCodes.Ldc_R8, value );
|
|
}
|
|
|
|
public void Load( char value )
|
|
{
|
|
Load( (int) value );
|
|
|
|
Pop();
|
|
Push( typeof( char ) );
|
|
}
|
|
|
|
public void Load( bool value )
|
|
{
|
|
Push( typeof( bool ) );
|
|
|
|
if ( value )
|
|
m_Generator.Emit( OpCodes.Ldc_I4_1 );
|
|
else
|
|
m_Generator.Emit( OpCodes.Ldc_I4_0 );
|
|
}
|
|
|
|
public void Load( int value )
|
|
{
|
|
Push( typeof( int ) );
|
|
|
|
switch ( value )
|
|
{
|
|
case -1:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_M1 );
|
|
break;
|
|
|
|
case 0:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_0 );
|
|
break;
|
|
|
|
case 1:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_1 );
|
|
break;
|
|
|
|
case 2:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_2 );
|
|
break;
|
|
|
|
case 3:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_3 );
|
|
break;
|
|
|
|
case 4:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_4 );
|
|
break;
|
|
|
|
case 5:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_5 );
|
|
break;
|
|
|
|
case 6:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_6 );
|
|
break;
|
|
|
|
case 7:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_7 );
|
|
break;
|
|
|
|
case 8:
|
|
m_Generator.Emit( OpCodes.Ldc_I4_8 );
|
|
break;
|
|
|
|
default:
|
|
if ( value >= sbyte.MinValue && value <= sbyte.MaxValue )
|
|
m_Generator.Emit( OpCodes.Ldc_I4_S, (sbyte) value );
|
|
else
|
|
m_Generator.Emit( OpCodes.Ldc_I4, value );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void LoadField( FieldInfo field )
|
|
{
|
|
Pop( field.DeclaringType );
|
|
|
|
Push( field.FieldType );
|
|
|
|
m_Generator.Emit( OpCodes.Ldfld, field );
|
|
}
|
|
|
|
public void LoadLocal( LocalBuilder local )
|
|
{
|
|
Push( local.LocalType );
|
|
|
|
int index = local.LocalIndex;
|
|
|
|
switch ( index )
|
|
{
|
|
case 0:
|
|
m_Generator.Emit( OpCodes.Ldloc_0 );
|
|
break;
|
|
|
|
case 1:
|
|
m_Generator.Emit( OpCodes.Ldloc_1 );
|
|
break;
|
|
|
|
case 2:
|
|
m_Generator.Emit( OpCodes.Ldloc_2 );
|
|
break;
|
|
|
|
case 3:
|
|
m_Generator.Emit( OpCodes.Ldloc_3 );
|
|
break;
|
|
|
|
default:
|
|
if ( index >= byte.MinValue && index <= byte.MinValue )
|
|
m_Generator.Emit( OpCodes.Ldloc_S, (byte) index );
|
|
else
|
|
m_Generator.Emit( OpCodes.Ldloc, (short) index );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void StoreLocal( LocalBuilder local )
|
|
{
|
|
Pop( local.LocalType );
|
|
|
|
m_Generator.Emit( OpCodes.Stloc, local );
|
|
}
|
|
|
|
public void LoadArgument( int index )
|
|
{
|
|
if ( index > 0 )
|
|
Push( m_ArgumentTypes[index - 1] );
|
|
else
|
|
Push( m_TypeBuilder );
|
|
|
|
switch ( index )
|
|
{
|
|
case 0:
|
|
m_Generator.Emit( OpCodes.Ldarg_0 );
|
|
break;
|
|
|
|
case 1:
|
|
m_Generator.Emit( OpCodes.Ldarg_1 );
|
|
break;
|
|
|
|
case 2:
|
|
m_Generator.Emit( OpCodes.Ldarg_2 );
|
|
break;
|
|
|
|
case 3:
|
|
m_Generator.Emit( OpCodes.Ldarg_3 );
|
|
break;
|
|
|
|
default:
|
|
if ( index >= byte.MinValue && index <= byte.MaxValue )
|
|
m_Generator.Emit( OpCodes.Ldarg_S, (byte) index );
|
|
else
|
|
m_Generator.Emit( OpCodes.Ldarg, (short) index );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void CastAs( Type type )
|
|
{
|
|
Pop( typeof( object ) );
|
|
Push( type );
|
|
|
|
m_Generator.Emit( OpCodes.Isinst, type );
|
|
}
|
|
|
|
public void Neg()
|
|
{
|
|
Pop( typeof( int ) );
|
|
|
|
Push( typeof( int ) );
|
|
|
|
m_Generator.Emit( OpCodes.Neg );
|
|
}
|
|
|
|
public void Compare( OpCode opCode )
|
|
{
|
|
Pop();
|
|
Pop();
|
|
|
|
Push( typeof( int ) );
|
|
|
|
m_Generator.Emit( opCode );
|
|
}
|
|
|
|
public void LogicalNot()
|
|
{
|
|
Pop( typeof( int ) );
|
|
|
|
Push( typeof( int ) );
|
|
|
|
m_Generator.Emit( OpCodes.Ldc_I4_0 );
|
|
m_Generator.Emit( OpCodes.Ceq );
|
|
}
|
|
|
|
public void Xor()
|
|
{
|
|
Pop( typeof( int ) );
|
|
Pop( typeof( int ) );
|
|
|
|
Push( typeof( int ) );
|
|
|
|
m_Generator.Emit( OpCodes.Xor );
|
|
}
|
|
|
|
public Type Active
|
|
{
|
|
get { return m_Stack.Peek(); }
|
|
}
|
|
|
|
public void Chain( Property prop )
|
|
{
|
|
for ( int i = 0; i < prop.Chain.Length; ++i )
|
|
Call( prop.Chain[i].GetGetMethod() );
|
|
}
|
|
|
|
public void Call( MethodInfo method )
|
|
{
|
|
BeginCall( method );
|
|
|
|
CallInfo call = m_Calls.Peek();
|
|
|
|
if ( call.parms.Length > 0 )
|
|
throw new InvalidOperationException( "Method requires parameters." );
|
|
|
|
FinishCall();
|
|
}
|
|
|
|
public delegate void Callback();
|
|
|
|
#if MONO
|
|
private static bool GenericComparator( Type type, object obj )
|
|
{
|
|
return ( type.IsGenericType )
|
|
&& ( type.GetGenericTypeDefinition() == typeof( IComparable<> ) )
|
|
&& ( type.GetGenericArguments()[0].IsAssignableFrom(obj as Type) );
|
|
}
|
|
#endif
|
|
|
|
public bool CompareTo( int sign, Callback argGenerator )
|
|
{
|
|
Type active = this.Active;
|
|
|
|
MethodInfo compareTo = active.GetMethod( "CompareTo", new Type[] { active } );
|
|
|
|
if ( compareTo == null )
|
|
{
|
|
/* This gets a little tricky...
|
|
*
|
|
* There's a scenario where we might be trying to use CompareTo on an interface
|
|
* which, while it doesn't explicitly implement CompareTo itself, is said to
|
|
* extend IComparable indirectly. The implementation is implicitly passed off
|
|
* to implementers...
|
|
*
|
|
* interface ISomeInterface : IComparable
|
|
* {
|
|
* void SomeMethod();
|
|
* }
|
|
*
|
|
* class SomeClass : ISomeInterface
|
|
* {
|
|
* void SomeMethod() { ... }
|
|
* int CompareTo( object other ) { ... }
|
|
* }
|
|
*
|
|
* In this case, calling ISomeInterface.GetMethod( "CompareTo" ) will return null.
|
|
*
|
|
* Bleh.
|
|
*/
|
|
|
|
#if MONO
|
|
Type[] ifaces = active.FindInterfaces( GenericComparator, active );
|
|
#else
|
|
Type[] ifaces = active.FindInterfaces( delegate( Type type, object obj )
|
|
{
|
|
return ( type.IsGenericType )
|
|
&& ( type.GetGenericTypeDefinition() == typeof( IComparable<> ) )
|
|
&& ( type.GetGenericArguments()[0].IsAssignableFrom( active ) );
|
|
}, null );
|
|
#endif
|
|
|
|
if ( ifaces.Length > 0 )
|
|
{
|
|
compareTo = ifaces[0].GetMethod( "CompareTo", new Type[] { active } );
|
|
}
|
|
else
|
|
{
|
|
ifaces = active.FindInterfaces( delegate( Type type, object obj )
|
|
{
|
|
return ( type == typeof( IComparable ) );
|
|
}, null );
|
|
|
|
if ( ifaces.Length > 0 )
|
|
compareTo = ifaces[0].GetMethod( "CompareTo", new Type[] { active } );
|
|
}
|
|
}
|
|
|
|
if ( compareTo == null )
|
|
return false;
|
|
|
|
if ( !active.IsValueType )
|
|
{
|
|
/* This object is a reference type, so we have to make it behave
|
|
*
|
|
* null.CompareTo( null ) = 0
|
|
* real.CompareTo( null ) = -1
|
|
* null.CompareTo( real ) = +1
|
|
*
|
|
*/
|
|
|
|
LocalBuilder aValue = AcquireTemp( active );
|
|
LocalBuilder bValue = AcquireTemp( active );
|
|
|
|
StoreLocal( aValue );
|
|
|
|
argGenerator();
|
|
|
|
StoreLocal( bValue );
|
|
|
|
/* if ( aValue == null )
|
|
* {
|
|
* if ( bValue == null )
|
|
* v = 0;
|
|
* else
|
|
* v = +1;
|
|
* }
|
|
* else if ( bValue == null )
|
|
* {
|
|
* v = -1;
|
|
* }
|
|
* else
|
|
* {
|
|
* v = aValue.CompareTo( bValue );
|
|
* }
|
|
*/
|
|
|
|
Label store = CreateLabel();
|
|
|
|
Label aNotNull = CreateLabel();
|
|
|
|
LoadLocal( aValue );
|
|
BranchIfTrue( aNotNull );
|
|
// if ( aValue == null )
|
|
{
|
|
Label bNotNull = CreateLabel();
|
|
|
|
LoadLocal( bValue );
|
|
BranchIfTrue( bNotNull );
|
|
// if ( bValue == null )
|
|
{
|
|
Load( 0 );
|
|
Pop( typeof( int ) );
|
|
Branch( store );
|
|
}
|
|
MarkLabel( bNotNull );
|
|
// else
|
|
{
|
|
Load( sign );
|
|
Pop( typeof( int ) );
|
|
Branch( store );
|
|
}
|
|
}
|
|
MarkLabel( aNotNull );
|
|
// else
|
|
{
|
|
Label bNotNull = CreateLabel();
|
|
|
|
LoadLocal( bValue );
|
|
BranchIfTrue( bNotNull );
|
|
// bValue == null
|
|
{
|
|
Load( -sign );
|
|
Pop( typeof( int ) );
|
|
Branch( store );
|
|
}
|
|
MarkLabel( bNotNull );
|
|
// else
|
|
{
|
|
LoadLocal( aValue );
|
|
BeginCall( compareTo );
|
|
|
|
LoadLocal( bValue );
|
|
ArgumentPushed();
|
|
|
|
FinishCall();
|
|
|
|
if ( sign == -1 )
|
|
Neg();
|
|
}
|
|
}
|
|
|
|
MarkLabel( store );
|
|
|
|
ReleaseTemp( aValue );
|
|
ReleaseTemp( bValue );
|
|
}
|
|
else
|
|
{
|
|
BeginCall( compareTo );
|
|
|
|
argGenerator();
|
|
|
|
ArgumentPushed();
|
|
|
|
FinishCall();
|
|
|
|
if ( sign == -1 )
|
|
Neg();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void BeginCall( MethodInfo method )
|
|
{
|
|
Type type;
|
|
|
|
if ( ( method.CallingConvention & CallingConventions.HasThis ) != 0 )
|
|
type = m_Stack.Peek();
|
|
else
|
|
type = method.DeclaringType;
|
|
|
|
m_Calls.Push( new CallInfo( type, method ) );
|
|
|
|
if ( type.IsValueType )
|
|
{
|
|
LocalBuilder temp = AcquireTemp( type );
|
|
|
|
m_Generator.Emit( OpCodes.Stloc, temp );
|
|
m_Generator.Emit( OpCodes.Ldloca, temp );
|
|
|
|
ReleaseTemp( temp );
|
|
}
|
|
}
|
|
|
|
public void FinishCall()
|
|
{
|
|
CallInfo call = m_Calls.Pop();
|
|
|
|
if ( ( call.type.IsValueType || call.type.IsByRef ) && call.method.DeclaringType != call.type )
|
|
m_Generator.Emit( OpCodes.Constrained, call.type );
|
|
|
|
if ( call.method.DeclaringType.IsValueType || call.method.IsStatic )
|
|
m_Generator.Emit( OpCodes.Call, call.method );
|
|
else
|
|
m_Generator.Emit( OpCodes.Callvirt, call.method );
|
|
|
|
for ( int i = call.parms.Length - 1; i >= 0; --i )
|
|
Pop( call.parms[i].ParameterType );
|
|
|
|
if ( ( call.method.CallingConvention & CallingConventions.HasThis ) != 0 )
|
|
Pop( call.method.DeclaringType );
|
|
|
|
if ( call.method.ReturnType != typeof( void ) )
|
|
Push( call.method.ReturnType );
|
|
}
|
|
|
|
public void ArgumentPushed()
|
|
{
|
|
CallInfo call = m_Calls.Peek();
|
|
|
|
ParameterInfo parm = call.parms[call.index++];
|
|
|
|
Type argumentType = m_Stack.Peek();
|
|
|
|
if ( !parm.ParameterType.IsAssignableFrom( argumentType ) )
|
|
throw new InvalidOperationException( "Parameter type mismatch." );
|
|
|
|
if ( argumentType.IsValueType && !parm.ParameterType.IsValueType )
|
|
m_Generator.Emit( OpCodes.Box, argumentType );
|
|
}
|
|
}
|
|
} |