Skip to content

Commit

Permalink
Make JsValue implement IConvertible (#1713)
Browse files Browse the repository at this point in the history
* allow disabling ExpandoObject conversion
  • Loading branch information
lahma authored Jan 1, 2024
1 parent 24b45d6 commit 1636506
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 3 deletions.
18 changes: 18 additions & 0 deletions Jint.Tests/Runtime/ExtensionMethods/ExtensionMethodsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,5 +305,23 @@ public void GenericExtensionMethodOnGenericTypeInstantiatedInJs()
Assert.Equal(false, baseObservableResult.Last);
}

[Fact]
public void CanProjectAndGroupWhenExpandoObjectWrappingDisabled()
{
var engine = new Engine(options =>
{
options.AllowClr().AddExtensionMethods(typeof(Enumerable));
// prevent ExpandoObject wrapping
options.Interop.CreateClrObject = null;
});
engine.Execute("var a = [ 2, 4 ];");

var selected = engine.Evaluate("JSON.stringify(a.Select(m => ({a:m,b:m})).ToArray());").AsString();
Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", selected);

var grouped1 = engine.Evaluate("a.GroupBy(m => ({a:m,b:m})).ToArray()").AsArray();
var grouped = engine.Evaluate("JSON.stringify(a.GroupBy(m => ({a:m,b:m})).Select(x => x.Key).ToArray());").AsString();
Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", grouped);
}
}
}
105 changes: 105 additions & 0 deletions Jint/Native/JsValue.Convertible.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Jint.Runtime;

namespace Jint.Native;

public partial class JsValue : IConvertible
{
TypeCode IConvertible.GetTypeCode()
{
var type = _type & ~InternalTypes.InternalFlags;
return type switch
{
InternalTypes.Boolean => TypeCode.Boolean,
InternalTypes.String => TypeCode.String,
InternalTypes.Number => TypeCode.Double,
InternalTypes.Integer => TypeCode.Int32,
InternalTypes.PrivateName => TypeCode.String,
InternalTypes.Undefined => TypeCode.Object,
InternalTypes.Null => TypeCode.Object,
InternalTypes.Object => TypeCode.Object,
InternalTypes.PlainObject => TypeCode.Object,
InternalTypes.Array => TypeCode.Object,
_ => TypeCode.Empty
};
}

bool IConvertible.ToBoolean(IFormatProvider? provider)
{
return this.AsBoolean();
}

byte IConvertible.ToByte(IFormatProvider? provider)
{
throw new NotImplementedException();
}

char IConvertible.ToChar(IFormatProvider? provider)
{
throw new NotImplementedException();
}

DateTime IConvertible.ToDateTime(IFormatProvider? provider)
{
throw new NotImplementedException();
}

decimal IConvertible.ToDecimal(IFormatProvider? provider)
{
throw new NotImplementedException();
}

double IConvertible.ToDouble(IFormatProvider? provider)
{
return this.AsNumber();
}

short IConvertible.ToInt16(IFormatProvider? provider)
{
throw new NotImplementedException();
}

int IConvertible.ToInt32(IFormatProvider? provider)
{
return this.AsInteger();
}

long IConvertible.ToInt64(IFormatProvider? provider)
{
throw new NotImplementedException();
}

sbyte IConvertible.ToSByte(IFormatProvider? provider)
{
throw new NotImplementedException();
}

float IConvertible.ToSingle(IFormatProvider? provider)
{
throw new NotImplementedException();
}

string IConvertible.ToString(IFormatProvider? provider)
{
return this.AsString();
}

object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
throw new NotImplementedException();
}

ushort IConvertible.ToUInt16(IFormatProvider? provider)
{
throw new NotImplementedException();
}

uint IConvertible.ToUInt32(IFormatProvider? provider)
{
throw new NotImplementedException();
}

ulong IConvertible.ToUInt64(IFormatProvider? provider)
{
throw new NotImplementedException();
}
}
2 changes: 1 addition & 1 deletion Jint/Native/JsValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace Jint.Native
{
public abstract class JsValue : IEquatable<JsValue>
public abstract partial class JsValue : IEquatable<JsValue>
{
public static readonly JsValue Undefined = new JsUndefined();
public static readonly JsValue Null = new JsNull();
Expand Down
8 changes: 7 additions & 1 deletion Jint/Native/Object/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,13 @@ private object ToObject(ObjectTraverseStack stack)
break;
}

var o = _engine.Options.Interop.CreateClrObject(this);
var func = _engine.Options.Interop.CreateClrObject;
if (func is null)
{
goto default;
}

var o = func(this);
foreach (var p in GetOwnProperties())
{
if (!p.Value.Enumerable)
Expand Down
2 changes: 1 addition & 1 deletion Jint/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ public class InteropOptions
/// <summary>
/// Strategy to create a CLR object to hold converted <see cref="ObjectInstance"/>.
/// </summary>
public Func<ObjectInstance, IDictionary<string, object?>> CreateClrObject = _ => new ExpandoObject();
public Func<ObjectInstance, IDictionary<string, object?>>? CreateClrObject = _ => new ExpandoObject();

/// <summary>
/// Strategy to create a CLR object from TypeReference.
Expand Down

0 comments on commit 1636506

Please sign in to comment.