#W# Initial Commit: Avatars Conquest
This commit is contained in:
commit
8eae46895e
7512 changed files with 416187 additions and 0 deletions
740
Scripts/Misc/Emitter.cs
Normal file
740
Scripts/Misc/Emitter.cs
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue