diff --git a/Jurassic/Compiler/Binders/BinderUtilities.cs b/Jurassic/Compiler/Binders/BinderUtilities.cs
index f2eac035..3e1a13b3 100644
--- a/Jurassic/Compiler/Binders/BinderUtilities.cs
+++ b/Jurassic/Compiler/Binders/BinderUtilities.cs
@@ -96,6 +96,7 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn
demeritPoints[i] += disqualification;
break;
+ case TypeCode.DBNull:
case TypeCode.DateTime:
case TypeCode.Object:
if (input == null || input == Undefined.Value)
@@ -115,7 +116,6 @@ public static int ResolveOverloads(RuntimeMethodHandle[] methodHandles, ScriptEn
case TypeCode.Empty:
- case TypeCode.DBNull:
throw new NotSupportedException(string.Format("{0} is not a supported parameter type.", outputType));
}
}
diff --git a/Jurassic/Compiler/Binders/ClrBinder.cs b/Jurassic/Compiler/Binders/ClrBinder.cs
index 39818a17..c0b66f39 100644
--- a/Jurassic/Compiler/Binders/ClrBinder.cs
+++ b/Jurassic/Compiler/Binders/ClrBinder.cs
@@ -191,8 +191,6 @@ internal static void EmitConversionToType(ILGenerator generator, Type toType, bo
generator.LoadInt32(0);
generator.Call(ReflectionHelpers.String_GetChars);
break;
- case TypeCode.DBNull:
- throw new NotSupportedException("DBNull is not a supported parameter type.");
case TypeCode.Decimal:
EmitConversion.ToNumber(generator, PrimitiveType.Any);
generator.NewObject(ReflectionHelpers.Decimal_Constructor_Double);
@@ -213,6 +211,7 @@ internal static void EmitConversionToType(ILGenerator generator, Type toType, bo
generator.ConvertToInt64();
break;
+ case TypeCode.DBNull:
case TypeCode.DateTime:
case TypeCode.Object:
// Check if the type must be unwrapped.
@@ -340,8 +339,6 @@ internal static void EmitConversionToObject(ILGenerator generator, Type fromType
generator.NewObject(ReflectionHelpers.String_Constructor_Char_Int);
break;
- case TypeCode.DBNull:
- throw new NotSupportedException("DBNull is not a supported return type.");
case TypeCode.Decimal:
generator.Call(ReflectionHelpers.Decimal_ToDouble);
generator.Box(typeof(double));
@@ -362,6 +359,7 @@ internal static void EmitConversionToObject(ILGenerator generator, Type fromType
generator.Box(typeof(double));
break;
+ case TypeCode.DBNull:
case TypeCode.DateTime:
case TypeCode.Object:
// Check if the type must be wrapped with a ClrInstanceWrapper.
diff --git a/Jurassic/Core/TypeComparer.cs b/Jurassic/Core/TypeComparer.cs
index 989591dc..82187169 100644
--- a/Jurassic/Core/TypeComparer.cs
+++ b/Jurassic/Core/TypeComparer.cs
@@ -20,14 +20,54 @@ public static class TypeComparer
{
x = x ?? Undefined.Value;
y = y ?? Undefined.Value;
+ if (x is sbyte)
+ x = (double)(sbyte)x;
+ if (x is byte)
+ x = (double)(byte)x;
+ if (x is char)
+ x = (double)(char)x;
+ if (x is short)
+ x = (double)(short)x;
+ if (x is ushort)
+ x = (double)(ushort)x;
if (x is int)
x = (double)(int)x;
if (x is uint)
x = (double)(uint)x;
+ if (x is long)
+ x = (double)(long)x;
+ if (x is ulong)
+ x = (double)(ulong)x;
+ if (x is Enum)
+ x = (double)(int)x;
+ if (x is float)
+ x = (double)(float)x;
+ if (x is decimal)
+ x = decimal.ToDouble((decimal)x);
+ if (y is sbyte)
+ y = (double)(sbyte)y;
+ if (y is byte)
+ y = (double)(byte)y;
+ if (y is char)
+ y = (double)(char)y;
+ if (y is short)
+ y = (double)(short)y;
+ if (y is ushort)
+ y = (double)(ushort)y;
if (y is int)
y = (double)(int)y;
if (y is uint)
y = (double)(uint)y;
+ if (y is long)
+ y = (double)(long)y;
+ if (y is ulong)
+ y = (double)(ulong)y;
+ if (y is Enum)
+ y = (double)(int)y;
+ if (y is float)
+ y = (double)(float)y;
+ if (y is decimal)
+ y = decimal.ToDouble((decimal)y);
if (x is ConcatenatedString)
x = x.ToString();
if (y is ConcatenatedString)
@@ -81,14 +121,50 @@ public static bool StrictEquals(object x, object y)
{
x = x ?? Undefined.Value;
y = y ?? Undefined.Value;
+ if (x is sbyte)
+ x = (double)(sbyte)x;
+ if (x is byte)
+ x = (double)(byte)x;
+ if (x is char)
+ x = (double)(char)x;
+ if (x is short)
+ x = (double)(short)x;
+ if (x is ushort)
+ x = (double)(ushort)x;
if (x is int)
x = (double)(int)x;
if (x is uint)
x = (double)(uint)x;
+ if (x is long)
+ x = (double)(long)x;
+ if (x is ulong)
+ x = (double)(ulong)x;
+ if (x is float)
+ x = (double)(float)x;
+ if (x is decimal)
+ x = decimal.ToDouble((decimal)x);
+ if (y is sbyte)
+ y = (double)(sbyte)y;
+ if (y is byte)
+ y = (double)(byte)y;
+ if (y is char)
+ y = (double)(char)y;
+ if (y is short)
+ y = (double)(short)y;
+ if (y is ushort)
+ y = (double)(ushort)y;
if (y is int)
y = (double)(int)y;
if (y is uint)
y = (double)(uint)y;
+ if (y is long)
+ y = (double)(long)y;
+ if (y is ulong)
+ y = (double)(ulong)y;
+ if (y is float)
+ y = (double)(float)y;
+ if (y is decimal)
+ y = decimal.ToDouble((decimal)y);
if (x is double && double.IsNaN((double)x) == true)
return false;
if (x is ConcatenatedString)
@@ -224,14 +300,54 @@ public static bool SameValue(object x, object y)
x = Undefined.Value;
if (y == null)
y = Undefined.Value;
+ if (x is sbyte)
+ x = (double)(sbyte)x;
+ if (x is byte)
+ x = (double)(byte)x;
+ if (x is char)
+ x = (double)(char)x;
+ if (x is short)
+ x = (double)(short)x;
+ if (x is ushort)
+ x = (double)(ushort)x;
if (x is int)
x = (double)(int)x;
if (x is uint)
x = (double)(uint)x;
+ if (x is long)
+ x = (double)(long)x;
+ if (x is ulong)
+ x = (double)(ulong)x;
+ if (x is Enum)
+ x = (double)(int)x;
+ if (x is float)
+ x = (double)(float)x;
+ if (x is decimal)
+ x = decimal.ToDouble((decimal)x);
+ if (y is sbyte)
+ y = (double)(sbyte)y;
+ if (y is byte)
+ y = (double)(byte)y;
+ if (y is char)
+ y = (double)(char)y;
+ if (y is short)
+ y = (double)(short)y;
+ if (y is ushort)
+ y = (double)(ushort)y;
if (y is int)
y = (double)(int)y;
if (y is uint)
y = (double)(uint)y;
+ if (y is long)
+ y = (double)(long)y;
+ if (y is ulong)
+ y = (double)(ulong)y;
+ if (y is Enum)
+ y = (double)(int)y;
+ if (y is float)
+ y = (double)(float)y;
+ if (y is decimal)
+ y = decimal.ToDouble((decimal)y);
if (x is double && (double) x == 0.0 && y is double && (double)y == 0.0)
if ((1 / (double)x) != (1 / (double)y))
return false;
@@ -260,14 +376,54 @@ public static bool SameValueZero(object x, object y)
x = Undefined.Value;
if (y == null)
y = Undefined.Value;
+ if (x is sbyte)
+ x = (double)(sbyte)x;
+ if (x is byte)
+ x = (double)(byte)x;
+ if (x is char)
+ x = (double)(char)x;
+ if (x is short)
+ x = (double)(short)x;
+ if (x is ushort)
+ x = (double)(ushort)x;
if (x is int)
x = (double)(int)x;
if (x is uint)
x = (double)(uint)x;
+ if (x is long)
+ x = (double)(long)x;
+ if (x is ulong)
+ x = (double)(ulong)x;
+ if (x is Enum)
+ x = (double)(int)x;
+ if (x is float)
+ x = (double)(float)x;
+ if (x is decimal)
+ x = decimal.ToDouble((decimal)x);
+ if (y is sbyte)
+ y = (double)(sbyte)y;
+ if (y is byte)
+ y = (double)(byte)y;
+ if (y is char)
+ y = (double)(char)y;
+ if (y is short)
+ y = (double)(short)y;
+ if (y is ushort)
+ y = (double)(ushort)y;
if (y is int)
y = (double)(int)y;
if (y is uint)
y = (double)(uint)y;
+ if (y is long)
+ y = (double)(long)y;
+ if (y is ulong)
+ y = (double)(ulong)y;
+ if (y is Enum)
+ y = (double)(int)y;
+ if (y is float)
+ y = (double)(float)y;
+ if (y is decimal)
+ y = decimal.ToDouble((decimal)y);
if (x is ConcatenatedString)
x = x.ToString();
if (y is ConcatenatedString)
diff --git a/Jurassic/Core/TypeConverter.cs b/Jurassic/Core/TypeConverter.cs
index 070bdaef..47fbcd69 100644
--- a/Jurassic/Core/TypeConverter.cs
+++ b/Jurassic/Core/TypeConverter.cs
@@ -80,17 +80,37 @@ public static bool ToBoolean(object value)
return false;
if (value is bool)
return (bool)value;
+ if (value is sbyte)
+ return ((sbyte)value) != 0;
+ if (value is byte)
+ return ((byte)value) != 0;
+ if (value is char)
+ return ((char)value) != 0;
+ if (value is short)
+ return ((short)value) != 0;
+ if (value is ushort)
+ return ((ushort)value) != 0;
if (value is int)
return ((int)value) != 0;
if (value is uint)
return ((uint)value) != 0;
+ if (value is long)
+ return ((long)value) != 0;
+ if (value is ulong)
+ return ((ulong)value) != 0;
+ if (value is Enum )
+ return ((int)value) != 0;
+ if (value is float)
+ return ((float)value) != 0 && float.IsNaN((float)value) == false;
if (value is double)
return ((double)value) != 0 && double.IsNaN((double)value) == false;
+ if (value is decimal)
+ return ((decimal)value) != 0;
if (value is string)
return ((string)value).Length > 0;
if (value is ConcatenatedString)
return ((ConcatenatedString)value).Length > 0;
- if (value is ObjectInstance)
+ if (value is Object && !(value is DBNull)) // All non null objects, except DBNull are converted to true
return true;
throw new ArgumentException(string.Format("Cannot convert object of type '{0}' to a boolean.", value.GetType()), nameof(value));
}
@@ -102,12 +122,32 @@ public static bool ToBoolean(object value)
/// A primitive number value.
public static double ToNumber(object value)
{
+ if (value is float)
+ return (double)(float)value;
if (value is double)
return (double)value;
+ if (value is decimal)
+ return decimal.ToDouble((decimal)value);
+ if (value is sbyte)
+ return (double)(sbyte)value;
+ if (value is byte)
+ return (double)(byte)value;
+ if (value is char)
+ return (double)(char)value;
+ if (value is short)
+ return (double)(short)value;
+ if (value is ushort)
+ return (double)(ushort)value;
if (value is int)
return (double)(int)value;
if (value is uint)
return (double)(uint)value;
+ if (value is long)
+ return (double)(long)value;
+ if (value is ulong)
+ return (double)(ulong)value;
+ if (value is Enum)
+ return (int)value;
if (value == null || value == Undefined.Value)
return double.NaN;
if (value == Null.Value)
@@ -146,11 +186,27 @@ public static string ToString(object value)
return "null";
if (value is bool)
return (bool)value ? "true" : "false";
+ if (value is sbyte)
+ return ((sbyte)value).ToString();
+ if (value is byte)
+ return ((byte)value).ToString();
+ if (value is char)
+ return ((char)value).ToString();
+ if (value is short)
+ return ((short)value).ToString();
+ if (value is ushort)
+ return ((ushort)value).ToString();
if (value is int)
return ((int)value).ToString();
if (value is uint)
return ((uint)value).ToString();
- if (value is double)
+ if (value is long)
+ return ((long)value).ToString();
+ if (value is ulong)
+ return ((ulong)value).ToString();
+ if (value is Enum)
+ return value.ToString();
+ if (value is double || value is float)
{
// Check if the value is in the cache.
double doubleValue = (double)value;
@@ -167,6 +223,8 @@ public static string ToString(object value)
return result;
}
+ if (value is decimal)
+ return ((decimal)value).ToString();
if (value is string)
return (string)value;
if (value is ConcatenatedString)
@@ -224,16 +282,40 @@ public static ObjectInstance ToObject(ScriptEngine engine, object value, int lin
ObjectInstance result;
if (value is bool)
result = engine.Boolean.Construct((bool)value);
+ else if (value is sbyte)
+ result = engine.Number.Construct((sbyte)value);
+ else if (value is byte)
+ result = engine.Number.Construct((byte)value);
+ else if (value is char)
+ result = engine.Number.Construct((char)value);
+ else if (value is short)
+ result = engine.Number.Construct((short)value);
+ else if (value is ushort)
+ result = engine.Number.Construct((ushort)value);
else if (value is int)
result = engine.Number.Construct((int)value);
else if (value is uint)
result = engine.Number.Construct((uint)value);
+ else if (value is long)
+ result = engine.Number.Construct((long)value);
+ else if (value is ulong)
+ result = engine.Number.Construct((ulong)value);
+ else if (value is Enum)
+ result = engine.Number.Construct((int)value);
+ else if (value is float)
+ result = engine.Number.Construct((float)value);
else if (value is double)
result = engine.Number.Construct((double)value);
+ else if (value is decimal)
+ result = engine.Number.Construct(decimal.ToDouble((decimal)value));
else if (value is string)
result = engine.String.Construct((string)value);
else if (value is ConcatenatedString)
result = engine.String.Construct(value.ToString());
+ else if (value is DateTime)
+ result = engine.Date.Construct((DateTime)value);
+ else if (value is object && engine.EnableExposedClrTypes)
+ result = new ClrInstanceWrapper(engine, value);
else
throw new ArgumentException(string.Format("Cannot convert object of type '{0}' to an object.", value.GetType()), nameof(value));
result.IsExtensible = false;
diff --git a/Jurassic/Core/TypeUtilities.cs b/Jurassic/Core/TypeUtilities.cs
index f87e41b0..df278f00 100644
--- a/Jurassic/Core/TypeUtilities.cs
+++ b/Jurassic/Core/TypeUtilities.cs
@@ -24,7 +24,12 @@ public static string TypeOf(object obj)
return "object";
if (obj is bool)
return "boolean";
- if (obj is double || obj is int || obj is uint)
+ if (obj is float || obj is double || obj is decimal ||
+ obj is sbyte || obj is byte || obj is char ||
+ obj is short || obj is ushort ||
+ obj is int || obj is uint ||
+ obj is long || obj is ulong ||
+ obj is Enum)
return "number";
if (obj is string || obj is ConcatenatedString)
return "string";
@@ -55,7 +60,12 @@ internal static bool IsUndefined(object obj)
/// otherwise.
internal static bool IsNumeric(object obj)
{
- return obj is double || obj is int || obj is uint;
+ return obj is float || obj is double || obj is decimal ||
+ obj is sbyte || obj is byte || obj is char ||
+ obj is short || obj is ushort ||
+ obj is int || obj is uint ||
+ obj is long || obj is ulong ||
+ obj is Enum;
}
///
@@ -78,7 +88,7 @@ internal static object NormalizeValue(object obj)
{
if (obj == null)
return Undefined.Value;
- else if (obj is double)
+ else if (obj is float || obj is double || obj is decimal)
{
var numericResult = (double)obj;
if (((int)numericResult) == numericResult)
@@ -95,6 +105,8 @@ internal static object NormalizeValue(object obj)
obj = ((ConcatenatedString)obj).ToString();
else if (obj is ClrInstanceWrapper)
obj = ((ClrInstanceWrapper)obj).WrappedInstance;
+ else if (obj is ClrInstanceTypeWrapper)
+ obj = ((ClrInstanceTypeWrapper)obj).WrappedType;
else if (obj is ClrStaticTypeWrapper)
obj = ((ClrStaticTypeWrapper)obj).WrappedType;
return obj;
@@ -178,7 +190,12 @@ public static bool IsPrimitive(object value)
return true;
var type = value.GetType();
return type == typeof(bool) ||
- type == typeof(int) || type == typeof(uint) || type == typeof(double) ||
+ type == typeof(sbyte) || type == typeof(byte) || type == typeof(char) ||
+ type == typeof(short) || type == typeof(ushort) ||
+ type == typeof(int) || type == typeof(uint) ||
+ type == typeof(long) || type == typeof(ulong) ||
+ type.IsEnum ||
+ type == typeof(float) || type == typeof(double) || type == typeof(decimal) ||
type == typeof(string) || type == typeof(ConcatenatedString) ||
type == typeof(Null) || type == typeof(Undefined) ||
type == typeof(SymbolInstance);
@@ -197,7 +214,12 @@ public static bool IsPrimitiveOrObject(object value)
return true;
var type = value.GetType();
return type == typeof(bool) ||
- type == typeof(int) || type == typeof(uint) || type == typeof(double) ||
+ type == typeof(sbyte) || type == typeof(byte) || type == typeof(char) ||
+ type == typeof(short) || type == typeof(ushort) ||
+ type == typeof(int) || type == typeof(uint) ||
+ type == typeof(long) || type == typeof(ulong) ||
+ type.IsEnum ||
+ type == typeof(float) || type == typeof(double) || type == typeof(decimal) ||
type == typeof(string) || type == typeof(ConcatenatedString) ||
type == typeof(Null) || type == typeof(Undefined) ||
typeof(ObjectInstance).IsAssignableFrom(type);
diff --git a/Jurassic/Library/Date/DateConstructor.cs b/Jurassic/Library/Date/DateConstructor.cs
index 2c2d6b8f..f95babe2 100644
--- a/Jurassic/Library/Date/DateConstructor.cs
+++ b/Jurassic/Library/Date/DateConstructor.cs
@@ -67,6 +67,15 @@ public DateInstance Construct(string dateStr)
return new DateInstance(this.InstancePrototype, dateStr);
}
+ ///
+ /// Creates a new Date object from a DateTime.
+ ///
+ /// A regular DateTime
+ public DateInstance Construct(DateTime date)
+ {
+ return new DateInstance(this.InstancePrototype, date);
+ }
+
///
/// Creates a new Date object from various date components, expressed in local time.
///
diff --git a/Jurassic/Library/Date/DateInstance.cs b/Jurassic/Library/Date/DateInstance.cs
index 11e77436..5dbd70f1 100644
--- a/Jurassic/Library/Date/DateInstance.cs
+++ b/Jurassic/Library/Date/DateInstance.cs
@@ -80,7 +80,7 @@ public DateInstance(ObjectInstance prototype, int year, int month, int day = 1,
///
/// The next object in the prototype chain.
/// The date to set the instance value to.
- private DateInstance(ObjectInstance prototype, DateTime dateTime)
+ public DateInstance(ObjectInstance prototype, DateTime dateTime)
: base(prototype)
{
this.value = dateTime;