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 m_Stack; private Stack m_Calls; private Dictionary> m_Temps; public MethodBuilder Method { get { return m_Builder; } } public MethodEmitter( TypeBuilder typeBuilder ) { m_TypeBuilder = typeBuilder; m_Temps = new Dictionary>(); m_Stack = new Stack(); m_Calls = new Stack(); } 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 list; if ( !m_Temps.TryGetValue( localType, out list ) ) m_Temps[localType] = list = new Queue(); if ( list.Count > 0 ) return list.Dequeue(); return CreateLocal( localType ); } public void ReleaseTemp( LocalBuilder local ) { Queue list; if ( !m_Temps.TryGetValue( local.LocalType, out list ) ) m_Temps[local.LocalType] = list = new Queue(); 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 ); } } }