/// <summary> /// Gets a relative weight of how much the conversion is matching the given types. /// Implementation must follow that of DynValueToObjectOfType.. it's not very DRY in that sense. /// However here we are in perf-sensitive path.. TODO : double-check the gain and see if a DRY impl is better. /// </summary> internal static int DynValueToObjectOfTypeWeight(DynValue value, Type desiredType, bool isOptional) { if (desiredType.IsByRef) { desiredType = desiredType.GetElementType(); } var customConverter = Script.GlobalOptions.CustomConverters.GetScriptToClrCustomConversion(value.Type, desiredType); if (customConverter != null) { return(WEIGHT_CUSTOM_CONVERTER_MATCH); } if (desiredType == typeof(DynValue)) { return(WEIGHT_EXACT_MATCH); } if (desiredType == typeof(object)) { return(WEIGHT_EXACT_MATCH); } StringConversions.StringSubtype stringSubType = StringConversions.GetStringSubtype(desiredType); Type nt = Nullable.GetUnderlyingType(desiredType); Type nullableType = null; if (nt != null) { nullableType = desiredType; desiredType = nt; } switch (value.Type) { case DataType.Void: if (isOptional) { return(WEIGHT_VOID_WITH_DEFAULT); } else if (desiredType.IsValueType || (nullableType != null)) { return(WEIGHT_VOID_WITHOUT_DEFAULT); } break; case DataType.Nil: if (desiredType.IsValueType) { if (nullableType != null) { return(WEIGHT_NIL_TO_NULLABLE); } if (isOptional) { return(WEIGHT_NIL_WITH_DEFAULT); } } else { return(WEIGHT_NIL_TO_REFTYPE); } break; case DataType.Boolean: if (desiredType == typeof(bool)) { return(WEIGHT_EXACT_MATCH); } if (stringSubType != StringConversions.StringSubtype.None) { return(WEIGHT_BOOL_TO_STRING); } break; case DataType.Number: if (desiredType.IsEnum) { // number to enum conv return(WEIGHT_NUMBER_TO_ENUM); } if (NumericConversions.NumericTypes.Contains(desiredType)) { return(GetNumericTypeWeight(desiredType)); } if (stringSubType != StringConversions.StringSubtype.None) { return(WEIGHT_NUMBER_TO_STRING); } break; case DataType.String: if (stringSubType == StringConversions.StringSubtype.String) { return(WEIGHT_EXACT_MATCH); } else if (stringSubType == StringConversions.StringSubtype.StringBuilder) { return(WEIGHT_STRING_TO_STRINGBUILDER); } else if (stringSubType == StringConversions.StringSubtype.Char) { return(WEIGHT_STRING_TO_CHAR); } break; case DataType.Function: if (desiredType == typeof(Closure)) { return(WEIGHT_EXACT_MATCH); } else if (desiredType == typeof(ScriptFunctionDelegate)) { return(WEIGHT_EXACT_MATCH); } break; case DataType.ClrFunction: if (desiredType == typeof(CallbackFunction)) { return(WEIGHT_EXACT_MATCH); } else if (desiredType == typeof(Func <ScriptExecutionContext, CallbackArguments, DynValue>)) { return(WEIGHT_EXACT_MATCH); } break; case DataType.UserData: if (value.UserData.Object != null) { var udObj = value.UserData.Object; var udDesc = value.UserData.Descriptor; if (udDesc.IsTypeCompatible(desiredType, udObj)) { return(WEIGHT_EXACT_MATCH); } if (stringSubType != StringConversions.StringSubtype.None) { return(WEIGHT_USERDATA_TO_STRING); } } break; case DataType.Table: if (desiredType == typeof(Table) || desiredType.IsAssignableFrom(typeof(Table))) { return(WEIGHT_EXACT_MATCH); } else if (TableConversions.CanConvertTableToType(value.Table, desiredType)) { return(WEIGHT_TABLE_CONVERSION); } break; case DataType.Tuple: break; } return(WEIGHT_NO_MATCH); }
/// <summary> /// Converts a DynValue to a CLR object of a specific type /// </summary> internal static object DynValueToObjectOfType(DynValue value, Type desiredType, object defaultValue, bool isOptional) { if (desiredType.IsByRef) { desiredType = desiredType.GetElementType(); } var converter = Script.GlobalOptions.CustomConverters.GetScriptToClrCustomConversion(value.Type, desiredType); if (converter != null) { var v = converter(value); if (v != null) { return(v); } } if (desiredType == typeof(DynValue)) { return(value); } if (desiredType == typeof(object)) { return(DynValueToObject(value)); } StringConversions.StringSubtype stringSubType = StringConversions.GetStringSubtype(desiredType); string str = null; Type nt = Nullable.GetUnderlyingType(desiredType); Type nullableType = null; if (nt != null) { nullableType = desiredType; desiredType = nt; } switch (value.Type) { case DataType.Void: if (isOptional) { return(defaultValue); } else if (!desiredType.IsValueType || (nullableType != null)) { return(null); } break; case DataType.Nil: if (desiredType.IsValueType) { if (nullableType != null) { return(null); } if (isOptional) { return(defaultValue); } } else { return(null); } break; case DataType.Boolean: if (desiredType == typeof(bool)) { return(value.Boolean); } if (stringSubType != StringConversions.StringSubtype.None) { str = value.Boolean.ToString(); } break; case DataType.Number: if (desiredType.IsEnum) { // number to enum conv Type underType = Enum.GetUnderlyingType(desiredType); return(NumericConversions.DoubleToType(underType, value.Number)); } if (NumericConversions.NumericTypes.Contains(desiredType)) { object d = NumericConversions.DoubleToType(desiredType, value.Number); if (d.GetType() == desiredType) { return(d); } break; } if (stringSubType != StringConversions.StringSubtype.None) { str = value.Number.ToString(); } break; case DataType.String: if (stringSubType != StringConversions.StringSubtype.None) { str = value.String; } break; case DataType.Function: if (desiredType == typeof(Closure)) { return(value.Function); } else if (desiredType == typeof(ScriptFunctionDelegate)) { return(value.Function.GetDelegate()); } break; case DataType.ClrFunction: if (desiredType == typeof(CallbackFunction)) { return(value.Callback); } else if (desiredType == typeof(Func <ScriptExecutionContext, CallbackArguments, DynValue>)) { return(value.Callback.ClrCallback); } break; case DataType.UserData: if (value.UserData.Object != null) { var udObj = value.UserData.Object; var udDesc = value.UserData.Descriptor; if (udDesc.IsTypeCompatible(desiredType, udObj)) { return(udObj); } if (stringSubType != StringConversions.StringSubtype.None) { str = udDesc.AsString(udObj); } } break; case DataType.Table: if (desiredType == typeof(Table) || desiredType.IsAssignableFrom(typeof(Table))) { return(value.Table); } else { object o = TableConversions.ConvertTableToType(value.Table, desiredType); if (o != null) { return(o); } } break; case DataType.Tuple: break; } if (stringSubType != StringConversions.StringSubtype.None && str != null) { return(StringConversions.ConvertString(stringSubType, str, desiredType, value.Type)); } throw ScriptRuntimeException.ConvertObjectFailed(value.Type, desiredType); }
//TODO use compiled lamda expresssion, which would GREATLY optimize this tree //the process of converting /// <summary> /// Converts a DynValue to a CLR object of a specific type /// </summary> internal static T DynValueToTypedValue <T>(DynValue value, T defaultValue, bool isOptional) { var desiredType = typeof(T); if (desiredType.IsByRef) { desiredType = desiredType.GetElementType(); } var converter = Script.GlobalOptions.CustomConverters.GetScriptToClrCustomConversion(value.Type, typeof(T)); if (converter != null) { var v = converter(value, desiredType); if (v != null) { return((T)v); } } if (desiredType == typeof(DynValue)) { return(ValueConverter <DynValue, T> .Instance.Convert(value)); } if (desiredType == typeof(object)) { return((T)DynValueToObject(value)); } StringConversions.StringSubtype stringSubType = StringConversions.GetStringSubtype(desiredType); string str = null; Type nt = Nullable.GetUnderlyingType(desiredType); Type nullableType = null; if (nt != null) { nullableType = desiredType; desiredType = nt; } switch (value.Type) { case DataType.Void: if (isOptional) { return(defaultValue); } else if ((!desiredType.IsValueType) || (nullableType != null)) { return(default(T)); } break; case DataType.Nil: if (desiredType.IsValueType) { if (nullableType != null) { return(default(T)); } if (isOptional) { return(defaultValue); } } else { return(default(T)); } break; case DataType.Boolean: if (desiredType == typeof(bool)) { return(ValueConverter <bool, T> .Instance.Convert(value.Boolean)); } if (stringSubType != StringConversions.StringSubtype.None) { str = value.Boolean.ToString(); } break; case DataType.Number: if (desiredType.IsEnum || NumericConversions.NumericTypes.Contains(desiredType)) { return(ValueConverter <double, T> .Instance.Convert(value.Number)); } if (stringSubType != StringConversions.StringSubtype.None) { str = value.Number.ToString(); } break; case DataType.String: if (stringSubType != StringConversions.StringSubtype.None) { str = value.String; } break; case DataType.Function: if (desiredType == typeof(Closure)) { return(ValueConverter <Closure, T> .Instance.Convert(value.Function)); } else if (desiredType == typeof(ScriptFunctionDelegate <T>)) { return(ValueConverter <ScriptFunctionDelegate <T>, T> .Instance.Convert(value.Function.GetDelegate <T>())); } break; case DataType.ClrFunction: if (desiredType == typeof(CallbackFunction)) { return(ValueConverter <CallbackFunction, T> .Instance.Convert(value.Callback)); } else if (desiredType == typeof(Func <ScriptExecutionContext, CallbackArguments, DynValue>)) { return(ValueConverter <Func <ScriptExecutionContext, CallbackArguments, DynValue>, T> .Instance.Convert(value.Callback.ClrCallback)); } break; case DataType.UserData: if (value.UserData.HasValue()) { T t; if (value.UserData.TryGet(out t)) { return(t); } if (stringSubType != StringConversions.StringSubtype.None) { str = value.UserData.AsString(); } } break; case DataType.Table: if (desiredType == typeof(Table) || Framework.Do.IsAssignableFrom(desiredType, typeof(Table))) { return(ValueConverter <Table, T> .Instance.Convert(value.Table)); } else { object o = TableConversions.ConvertTableToType(value.Table, typeof(T)); if (o != null) { return(ValueConverter <object, T> .Instance.Convert(o)); } } break; case DataType.Tuple: break; } if (stringSubType != StringConversions.StringSubtype.None && str != null) { return(ValueConverter <object, T> .Instance.Convert(StringConversions.ConvertString(stringSubType, str, desiredType, value.Type))); } throw ScriptRuntimeException.ConvertObjectFailed(value.Type, desiredType); }