569 lines
No EOL
12 KiB
C#
569 lines
No EOL
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Text;
|
|
using Server;
|
|
|
|
namespace Server.Commands.Generic
|
|
{
|
|
public interface IConditional
|
|
{
|
|
bool Verify( object obj );
|
|
}
|
|
|
|
public interface ICondition
|
|
{
|
|
// Invoked during the constructor
|
|
void Construct( TypeBuilder typeBuilder, ILGenerator il, int index );
|
|
|
|
// Target object will be loaded on the stack
|
|
void Compile( MethodEmitter emitter );
|
|
}
|
|
|
|
public sealed class TypeCondition : ICondition
|
|
{
|
|
public static TypeCondition Default = new TypeCondition();
|
|
|
|
void ICondition.Construct( TypeBuilder typeBuilder, ILGenerator il, int index )
|
|
{
|
|
}
|
|
|
|
void ICondition.Compile( MethodEmitter emitter )
|
|
{
|
|
// The object was safely cast to be the conditionals type
|
|
// If it's null, then the type cast didn't work...
|
|
|
|
emitter.LoadNull();
|
|
emitter.Compare( OpCodes.Ceq );
|
|
emitter.LogicalNot();
|
|
}
|
|
}
|
|
|
|
public sealed class PropertyValue
|
|
{
|
|
private Type m_Type;
|
|
private object m_Value;
|
|
private FieldInfo m_Field;
|
|
|
|
public Type Type
|
|
{
|
|
get { return m_Type; }
|
|
}
|
|
|
|
public object Value
|
|
{
|
|
get { return m_Value; }
|
|
}
|
|
|
|
public FieldInfo Field
|
|
{
|
|
get { return m_Field; }
|
|
}
|
|
|
|
public bool HasField
|
|
{
|
|
get { return ( m_Field != null ); }
|
|
}
|
|
|
|
public PropertyValue( Type type, object value )
|
|
{
|
|
m_Type = type;
|
|
m_Value = value;
|
|
}
|
|
|
|
public void Load( MethodEmitter method )
|
|
{
|
|
if ( m_Field != null )
|
|
{
|
|
method.LoadArgument( 0 );
|
|
method.LoadField( m_Field );
|
|
}
|
|
else if ( m_Value == null )
|
|
{
|
|
method.LoadNull( m_Type );
|
|
}
|
|
else
|
|
{
|
|
if ( m_Value is int )
|
|
method.Load( (int) m_Value );
|
|
else if ( m_Value is long )
|
|
method.Load( (long) m_Value );
|
|
else if ( m_Value is float )
|
|
method.Load( (float) m_Value );
|
|
else if ( m_Value is double )
|
|
method.Load( (double) m_Value );
|
|
else if ( m_Value is char )
|
|
method.Load( (char) m_Value );
|
|
else if ( m_Value is bool )
|
|
method.Load( (bool) m_Value );
|
|
else if ( m_Value is string )
|
|
method.Load( (string) m_Value );
|
|
else if ( m_Value is Enum )
|
|
method.Load( (Enum) m_Value );
|
|
else
|
|
throw new InvalidOperationException( "Unrecognized comparison value." );
|
|
}
|
|
}
|
|
|
|
public void Acquire( TypeBuilder typeBuilder, ILGenerator il, string fieldName )
|
|
{
|
|
if ( m_Value is string )
|
|
{
|
|
string toParse = (string) m_Value;
|
|
|
|
if ( !m_Type.IsValueType && toParse == "null" )
|
|
{
|
|
m_Value = null;
|
|
}
|
|
else if ( m_Type == typeof( string ) )
|
|
{
|
|
if ( toParse == @"@""null""" )
|
|
toParse = "null";
|
|
|
|
m_Value = toParse;
|
|
}
|
|
else if ( m_Type.IsEnum )
|
|
{
|
|
m_Value = Enum.Parse( m_Type, toParse, true );
|
|
}
|
|
else
|
|
{
|
|
MethodInfo parseMethod = null;
|
|
object[] parseArgs = null;
|
|
|
|
MethodInfo parseNumber = m_Type.GetMethod(
|
|
"Parse",
|
|
BindingFlags.Public | BindingFlags.Static,
|
|
null,
|
|
new Type[] { typeof( string ), typeof( NumberStyles ) },
|
|
null
|
|
);
|
|
|
|
if ( parseNumber != null )
|
|
{
|
|
NumberStyles style = NumberStyles.Integer;
|
|
|
|
if ( Insensitive.StartsWith( toParse, "0x" ) )
|
|
{
|
|
style = NumberStyles.HexNumber;
|
|
toParse = toParse.Substring( 2 );
|
|
}
|
|
|
|
parseMethod = parseNumber;
|
|
parseArgs = new object[] { toParse, style };
|
|
}
|
|
else
|
|
{
|
|
MethodInfo parseGeneral = m_Type.GetMethod(
|
|
"Parse",
|
|
BindingFlags.Public | BindingFlags.Static,
|
|
null,
|
|
new Type[] { typeof( string ) },
|
|
null
|
|
);
|
|
|
|
parseMethod = parseGeneral;
|
|
parseArgs = new object[] { toParse };
|
|
}
|
|
|
|
if ( parseMethod != null )
|
|
{
|
|
m_Value = parseMethod.Invoke( null, parseArgs );
|
|
|
|
if ( !m_Type.IsPrimitive )
|
|
{
|
|
m_Field = typeBuilder.DefineField(
|
|
fieldName,
|
|
m_Type,
|
|
FieldAttributes.Private | FieldAttributes.InitOnly
|
|
);
|
|
|
|
il.Emit( OpCodes.Ldarg_0 );
|
|
|
|
il.Emit( OpCodes.Ldstr, toParse );
|
|
|
|
if ( parseArgs.Length == 2 ) // dirty evil hack :-(
|
|
il.Emit( OpCodes.Ldc_I4, (int) parseArgs[1] );
|
|
|
|
il.Emit( OpCodes.Call, parseMethod );
|
|
il.Emit( OpCodes.Stfld, m_Field );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException(
|
|
String.Format(
|
|
"Unable to convert string \"{0}\" into type '{1}'.",
|
|
m_Value,
|
|
m_Type
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public abstract class PropertyCondition : ICondition
|
|
{
|
|
protected Property m_Property;
|
|
protected bool m_Not;
|
|
|
|
public PropertyCondition( Property property, bool not )
|
|
{
|
|
m_Property = property;
|
|
m_Not = not;
|
|
}
|
|
|
|
public abstract void Construct( TypeBuilder typeBuilder, ILGenerator il, int index );
|
|
|
|
public abstract void Compile( MethodEmitter emitter );
|
|
}
|
|
|
|
public enum StringOperator
|
|
{
|
|
Equal,
|
|
NotEqual,
|
|
|
|
Contains,
|
|
|
|
StartsWith,
|
|
EndsWith
|
|
}
|
|
|
|
public sealed class StringCondition : PropertyCondition
|
|
{
|
|
private StringOperator m_Operator;
|
|
private PropertyValue m_Value;
|
|
|
|
private bool m_IgnoreCase;
|
|
|
|
public StringCondition( Property property, bool not, StringOperator op, object value, bool ignoreCase )
|
|
: base( property, not )
|
|
{
|
|
m_Operator = op;
|
|
m_Value = new PropertyValue( property.Type, value );
|
|
|
|
m_IgnoreCase = ignoreCase;
|
|
}
|
|
|
|
public override void Construct( TypeBuilder typeBuilder, ILGenerator il, int index )
|
|
{
|
|
m_Value.Acquire( typeBuilder, il, "v" + index );
|
|
}
|
|
|
|
public override void Compile( MethodEmitter emitter )
|
|
{
|
|
bool inverse = false;
|
|
|
|
string methodName;
|
|
|
|
switch ( m_Operator )
|
|
{
|
|
case StringOperator.Equal:
|
|
methodName = "Equals";
|
|
break;
|
|
|
|
case StringOperator.NotEqual:
|
|
methodName = "Equals";
|
|
inverse = true;
|
|
break;
|
|
|
|
case StringOperator.Contains:
|
|
methodName = "Contains";
|
|
break;
|
|
|
|
case StringOperator.StartsWith:
|
|
methodName = "StartsWith";
|
|
break;
|
|
|
|
case StringOperator.EndsWith:
|
|
methodName = "EndsWith";
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException( "Invalid string comparison operator." );
|
|
}
|
|
|
|
if ( m_IgnoreCase || methodName == "Equals" )
|
|
{
|
|
Type type = ( m_IgnoreCase ? typeof( Insensitive ) : typeof( String ) );
|
|
|
|
emitter.BeginCall(
|
|
type.GetMethod(
|
|
methodName,
|
|
BindingFlags.Public | BindingFlags.Static,
|
|
null,
|
|
new Type[]
|
|
{
|
|
typeof( string ),
|
|
typeof( string )
|
|
},
|
|
null
|
|
)
|
|
);
|
|
|
|
emitter.Chain( m_Property );
|
|
m_Value.Load( emitter );
|
|
|
|
emitter.FinishCall();
|
|
}
|
|
else
|
|
{
|
|
Label notNull = emitter.CreateLabel();
|
|
Label moveOn = emitter.CreateLabel();
|
|
|
|
LocalBuilder temp = emitter.AcquireTemp( m_Property.Type );
|
|
|
|
emitter.Chain( m_Property );
|
|
|
|
emitter.StoreLocal( temp );
|
|
emitter.LoadLocal( temp );
|
|
|
|
emitter.BranchIfTrue( notNull );
|
|
|
|
emitter.Load( false );
|
|
emitter.Pop();
|
|
emitter.Branch( moveOn );
|
|
|
|
emitter.MarkLabel( notNull );
|
|
emitter.LoadLocal( temp );
|
|
|
|
emitter.BeginCall(
|
|
typeof( string ).GetMethod(
|
|
methodName,
|
|
BindingFlags.Public | BindingFlags.Instance,
|
|
null,
|
|
new Type[]
|
|
{
|
|
typeof( string )
|
|
},
|
|
null
|
|
)
|
|
);
|
|
|
|
m_Value.Load( emitter );
|
|
|
|
emitter.FinishCall();
|
|
|
|
emitter.MarkLabel( moveOn );
|
|
}
|
|
|
|
if ( m_Not != inverse )
|
|
emitter.LogicalNot();
|
|
}
|
|
}
|
|
|
|
public enum ComparisonOperator
|
|
{
|
|
Equal,
|
|
NotEqual,
|
|
Greater,
|
|
GreaterEqual,
|
|
Lesser,
|
|
LesserEqual
|
|
}
|
|
|
|
public sealed class ComparisonCondition : PropertyCondition
|
|
{
|
|
private ComparisonOperator m_Operator;
|
|
private PropertyValue m_Value;
|
|
|
|
public ComparisonCondition( Property property, bool not, ComparisonOperator op, object value )
|
|
: base( property, not )
|
|
{
|
|
m_Operator = op;
|
|
m_Value = new PropertyValue( property.Type, value );
|
|
}
|
|
|
|
public override void Construct( TypeBuilder typeBuilder, ILGenerator il, int index )
|
|
{
|
|
m_Value.Acquire( typeBuilder, il, "v" + index );
|
|
}
|
|
|
|
public override void Compile( MethodEmitter emitter )
|
|
{
|
|
emitter.Chain( m_Property );
|
|
|
|
bool inverse = false;
|
|
|
|
bool couldCompare =
|
|
emitter.CompareTo( 1, delegate()
|
|
{
|
|
m_Value.Load( emitter );
|
|
} );
|
|
|
|
if ( couldCompare )
|
|
{
|
|
emitter.Load( 0 );
|
|
|
|
switch ( m_Operator )
|
|
{
|
|
case ComparisonOperator.Equal:
|
|
emitter.Compare( OpCodes.Ceq );
|
|
break;
|
|
|
|
case ComparisonOperator.NotEqual:
|
|
emitter.Compare( OpCodes.Ceq );
|
|
inverse = true;
|
|
break;
|
|
|
|
case ComparisonOperator.Greater:
|
|
emitter.Compare( OpCodes.Cgt );
|
|
break;
|
|
|
|
case ComparisonOperator.GreaterEqual:
|
|
emitter.Compare( OpCodes.Clt );
|
|
inverse = true;
|
|
break;
|
|
|
|
case ComparisonOperator.Lesser:
|
|
emitter.Compare( OpCodes.Clt );
|
|
break;
|
|
|
|
case ComparisonOperator.LesserEqual:
|
|
emitter.Compare( OpCodes.Cgt );
|
|
inverse = true;
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException( "Invalid comparison operator." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This type is -not- comparable
|
|
// We can only support == and != operations
|
|
|
|
m_Value.Load( emitter );
|
|
|
|
switch ( m_Operator )
|
|
{
|
|
case ComparisonOperator.Equal:
|
|
emitter.Compare( OpCodes.Ceq );
|
|
break;
|
|
|
|
case ComparisonOperator.NotEqual:
|
|
emitter.Compare( OpCodes.Ceq );
|
|
inverse = true;
|
|
break;
|
|
|
|
case ComparisonOperator.Greater:
|
|
case ComparisonOperator.GreaterEqual:
|
|
case ComparisonOperator.Lesser:
|
|
case ComparisonOperator.LesserEqual:
|
|
throw new InvalidOperationException( "Property does not support relational comparisons." );
|
|
|
|
default:
|
|
throw new InvalidOperationException( "Invalid operator." );
|
|
}
|
|
}
|
|
|
|
if ( m_Not != inverse )
|
|
emitter.LogicalNot();
|
|
}
|
|
}
|
|
|
|
public static class ConditionalCompiler
|
|
{
|
|
public static IConditional Compile( AssemblyEmitter assembly, Type objectType, ICondition[] conditions, int index )
|
|
{
|
|
TypeBuilder typeBuilder = assembly.DefineType(
|
|
"__conditional" + index,
|
|
TypeAttributes.Public,
|
|
typeof( object )
|
|
);
|
|
|
|
#region Constructor
|
|
{
|
|
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
|
|
MethodAttributes.Public,
|
|
CallingConventions.Standard,
|
|
Type.EmptyTypes
|
|
);
|
|
|
|
ILGenerator il = ctor.GetILGenerator();
|
|
|
|
// : base()
|
|
il.Emit( OpCodes.Ldarg_0 );
|
|
il.Emit( OpCodes.Call, typeof( object ).GetConstructor( Type.EmptyTypes ) );
|
|
|
|
for ( int i = 0; i < conditions.Length; ++i )
|
|
conditions[i].Construct( typeBuilder, il, i );
|
|
|
|
// return;
|
|
il.Emit( OpCodes.Ret );
|
|
}
|
|
#endregion
|
|
|
|
#region IComparer
|
|
typeBuilder.AddInterfaceImplementation( typeof( IConditional ) );
|
|
|
|
MethodBuilder compareMethod;
|
|
|
|
#region Compare
|
|
{
|
|
MethodEmitter emitter = new MethodEmitter( typeBuilder );
|
|
|
|
emitter.Define(
|
|
/* name */ "Verify",
|
|
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
|
|
/* return */ typeof( bool ),
|
|
/* params */ new Type[] { typeof( object ) } );
|
|
|
|
LocalBuilder obj = emitter.CreateLocal( objectType );
|
|
LocalBuilder eq = emitter.CreateLocal( typeof( bool ) );
|
|
|
|
emitter.LoadArgument( 1 );
|
|
emitter.CastAs( objectType );
|
|
emitter.StoreLocal( obj );
|
|
|
|
Label done = emitter.CreateLabel();
|
|
|
|
for ( int i = 0; i < conditions.Length; ++i )
|
|
{
|
|
if ( i > 0 )
|
|
{
|
|
emitter.LoadLocal( eq );
|
|
|
|
emitter.BranchIfFalse( done );
|
|
}
|
|
|
|
emitter.LoadLocal( obj );
|
|
|
|
conditions[i].Compile( emitter );
|
|
|
|
emitter.StoreLocal( eq );
|
|
}
|
|
|
|
emitter.MarkLabel( done );
|
|
|
|
emitter.LoadLocal( eq );
|
|
|
|
emitter.Return();
|
|
|
|
typeBuilder.DefineMethodOverride(
|
|
emitter.Method,
|
|
typeof( IConditional ).GetMethod(
|
|
"Verify",
|
|
new Type[]
|
|
{
|
|
typeof( object )
|
|
}
|
|
)
|
|
);
|
|
|
|
compareMethod = emitter.Method;
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
Type conditionalType = typeBuilder.CreateType();
|
|
|
|
return (IConditional) Activator.CreateInstance( conditionalType );
|
|
}
|
|
}
|
|
} |