Skip to content

Commit

Permalink
Make CLR helper functions as member of ClrHelper object that be inj…
Browse files Browse the repository at this point in the history
…ected into Engine.

Misc changes.
  • Loading branch information
viruscamp committed Aug 9, 2023
1 parent dbbd1b9 commit ea0485b
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 341 deletions.
444 changes: 221 additions & 223 deletions Jint.Tests/Runtime/InteropExplicitTypeTests.cs

Large diffs are not rendered by default.

39 changes: 3 additions & 36 deletions Jint/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,39 +120,9 @@ internal void Apply(Engine engine)
(thisObj, arguments) =>
new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))),
PropertyFlag.AllForbidden));
engine.Realm.GlobalObject.SetProperty("clrToString", new PropertyDescriptor(new ClrFunctionInstance(
engine,
"clrToString",
ClrHelper.ClrToString),
PropertyFlag.AllForbidden));
engine.Realm.GlobalObject.SetProperty("clrUnwrap", new PropertyDescriptor(new ClrFunctionInstance(
engine,
"clrUnwrap",
ClrHelper.ClrUnwrap),
PropertyFlag.AllForbidden));
engine.Realm.GlobalObject.SetProperty("clrWrap", new PropertyDescriptor(new ClrFunctionInstance(
engine,
"clrWrap",
ClrHelper.ClrWrap),
engine.Realm.GlobalObject.SetProperty("clrHelper", new PropertyDescriptor(
new ObjectWrapper(engine, new ClrHelper(Interop)),
PropertyFlag.AllForbidden));
if (Interop.AllowGetType)
{
engine.Realm.GlobalObject.SetProperty("clrType", new PropertyDescriptor(new ClrFunctionInstance(
engine,
"clrType",
ClrHelper.ClrType),
PropertyFlag.AllForbidden));
engine.Realm.GlobalObject.SetProperty("clrTypeToObject", new PropertyDescriptor(new ClrFunctionInstance(
engine,
"clrTypeToObject",
ClrHelper.ClrTypeToObject),
PropertyFlag.AllForbidden));
engine.Realm.GlobalObject.SetProperty("clrObjectToType", new PropertyDescriptor(new ClrFunctionInstance(
engine,
"clrObjectToType",
ClrHelper.ClrObjectToType),
PropertyFlag.AllForbidden));
}
}

if (Interop.ExtensionMethodTypes.Count > 0)
Expand Down Expand Up @@ -203,10 +173,7 @@ private static void AttachExtensionMethodsToPrototype(Engine engine, ObjectInsta
string name = overloads.Key;
PropertyDescriptor CreateMethodInstancePropertyDescriptor(ClrFunctionInstance? function)
{
var instance = function is null
? new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()), name)
: new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()), name, function);

var instance = new MethodInfoFunctionInstance(engine, objectType, name, MethodDescriptor.Build(overloads.ToList()), function);
return new PropertyDescriptor(instance, PropertyFlag.AllForbidden);
}

Expand Down
115 changes: 68 additions & 47 deletions Jint/Runtime/Interop/ClrHelper.cs
Original file line number Diff line number Diff line change
@@ -1,64 +1,85 @@
using Jint.Native;
namespace Jint.Runtime.Interop;

namespace Jint.Runtime.Interop
using Jint.Native;

public class ClrHelper
{
public static class ClrHelper
private readonly InteropOptions _interopOptions;

internal ClrHelper(InteropOptions interopOptions)
{
public static JsValue ClrUnwrap(JsValue thisObject, JsValue[] arguments)
{
var arg = arguments.At(0);
if (arg is ObjectWrapper obj)
{
return new ObjectWrapper(obj.Engine, obj.Target);
}
return arg;
}
_interopOptions = interopOptions;
}

public static JsValue ClrWrap(JsValue thisObject, JsValue[] arguments)
{
var arg = arguments.At(0);
if (arg is ObjectWrapper obj && arguments.At(1) is TypeReference type)
{
return new ObjectWrapper(obj.Engine, obj.Target, type.ReferenceType);
}
return arg;
}
/// <summary>
/// Call JsValue.ToString(), mainly for NamespaceReference.
/// </summary>
public JsValue ToString(JsValue value)
{
return value.ToString();
}

public static JsValue ClrType(JsValue thisObject, JsValue[] arguments)
{
var arg = arguments.At(0);
if (arg is ObjectWrapper obj)
{
return TypeReference.CreateTypeReference(obj.Engine, obj.ClrType);
}
return JsValue.Undefined;
}
/// <summary>
/// Cast `obj as ISomeInterface` to `obj`
/// </summary>
public JsValue Unwrap(ObjectWrapper obj)
{
return new ObjectWrapper(obj.Engine, obj.Target);
}

public static JsValue ClrToString(JsValue thisObject, JsValue[] arguments)
/// <summary>
/// Cast `obj` to `obj as ISomeInterface`
/// </summary>
public JsValue Wrap(ObjectWrapper obj, TypeReference type)
{
if (!type.ReferenceType.IsInstanceOfType(obj.Target))
{
return arguments.At(0).ToString();
ExceptionHelper.ThrowTypeError(type.Engine.Realm, "Argument obj must be an instance of type");
}
return new ObjectWrapper(obj.Engine, obj.Target, type.ReferenceType);
}

/// <summary>
/// Get `TypeReference(ISomeInterface)` from `obj as ISomeInterface`
/// </summary>
public JsValue TypeOf(ObjectWrapper obj)
{
MustAllowGetType();
return TypeReference.CreateTypeReference(obj.Engine, obj.ClrType);
}

/// <summary>
/// Cast `TypeReference(SomeClass)` to `ObjectWrapper(SomeClass)`
/// </summary>
public JsValue TypeToObject(TypeReference type)
{
MustAllowGetType();
var engine = type.Engine;
return engine.Options.Interop.WrapObjectHandler.Invoke(engine, type.ReferenceType, null) ?? JsValue.Undefined;
}

public static JsValue ClrTypeToObject(JsValue thisObject, JsValue[] arguments)
/// <summary>
/// Cast `ObjectWrapper(SomeClass)` to `TypeReference(SomeClass)`
/// </summary>
public JsValue ObjectToType(ObjectWrapper obj)
{
MustAllowGetType();
if (obj.Target is Type t)
{
return TypeReference.CreateTypeReference(obj.Engine, t);
}
else
{
var arg = arguments.At(0);
if (arg is TypeReference tr)
{
var engine = tr.Engine;
return engine.Options.Interop.WrapObjectHandler.Invoke(engine, tr.ReferenceType, null) ?? JsValue.Undefined;
}
return JsValue.Undefined;
ExceptionHelper.ThrowArgumentException("Must be an ObjectWrapper of Type", "obj");
}
return JsValue.Undefined;
}

public static JsValue ClrObjectToType(JsValue thisObject, JsValue[] arguments)
private void MustAllowGetType()
{
if (!_interopOptions.AllowGetType)
{
var arg = arguments.At(0);
if (arg is ObjectWrapper obj && obj.Target is Type t)
{
return TypeReference.CreateTypeReference(obj.Engine, t);
}
return JsValue.Undefined;
ExceptionHelper.ThrowInvalidOperationException("Invalid when Engine.Options.Interop.AllowGetType == false");
}
}
}
23 changes: 16 additions & 7 deletions Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ namespace Jint.Runtime.Interop
{
internal sealed class MethodInfoFunctionInstance : FunctionInstance
{
private readonly Type _targetType;
private readonly string _name;
private readonly MethodDescriptor[] _methods;
private readonly ClrFunctionInstance? _fallbackClrFunctionInstance;

public MethodInfoFunctionInstance(Engine engine, MethodDescriptor[] methods, string name)
public MethodInfoFunctionInstance(
Engine engine,
Type targetType,
string name,
MethodDescriptor[] methods,
ClrFunctionInstance? fallbackClrFunctionInstance = null)
: base(engine, engine.Realm, new JsString(name))
{
_targetType = targetType;
_name = name;
_methods = methods;
_prototype = engine.Realm.Intrinsics.Function.PrototypeObject;
}

public MethodInfoFunctionInstance(Engine engine, MethodDescriptor[] methods, string name, ClrFunctionInstance fallbackClrFunctionInstance)
: this(engine, methods, name)
{
_fallbackClrFunctionInstance = fallbackClrFunctionInstance;
_prototype = engine.Realm.Intrinsics.Function.PrototypeObject;
}

private static bool IsGenericParameter(object argObj, Type parameterType)
Expand Down Expand Up @@ -284,5 +288,10 @@ private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, MethodDescriptor me
newArgumentsCollection[nonParamsArgumentsCount] = jsArray;
return newArgumentsCollection;
}

public override string ToString()
{
return $"function {_targetType}.{_name}() {{ [native code] }}";
}
}
}
4 changes: 2 additions & 2 deletions Jint/Runtime/Interop/NamespaceReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public JsValue GetPath(string path)
// and only then in mscorlib. Probelm usage: System.IO.File.CreateText

// search in loaded assemblies
var lookupAssemblies = new[] {Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly()};
var lookupAssemblies = new[] { Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly() };

foreach (var assembly in lookupAssemblies)
{
Expand Down Expand Up @@ -192,7 +192,7 @@ public override PropertyDescriptor GetOwnProperty(JsValue property)

public override string ToString()
{
return "[clr namespace: " + _path + "]";
return "[CLR namespace: " + _path + "]";
}
}
}
8 changes: 4 additions & 4 deletions Jint/Runtime/Interop/ObjectWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public ObjectWrapper(Engine engine, object obj, Type? type = null)
}

public object Target { get; }
public Type ClrType { get; }
internal Type ClrType { get; }

public override bool IsArrayLike => _typeDescriptor.IsArrayLike;

Expand Down Expand Up @@ -254,15 +254,15 @@ public static PropertyDescriptor GetPropertyDescriptor(Engine engine, object tar
return member switch
{
PropertyInfo pi => new PropertyAccessor(pi.Name, pi),
MethodBase mb => new MethodAccessor(MethodDescriptor.Build(new[] {mb}), member.Name),
MethodBase mb => new MethodAccessor(target.GetType(), member.Name, MethodDescriptor.Build(new[] { mb })),
FieldInfo fi => new FieldAccessor(fi),
_ => null
};
}
return engine.Options.Interop.TypeResolver.GetAccessor(engine, target.GetType(), member.Name, Factory).CreatePropertyDescriptor(engine, target);
}

public static Type GetClrType(object obj, Type? type)
internal static Type GetClrType(object obj, Type? type)
{
if (type is null || type == typeof(object))
{
Expand Down Expand Up @@ -368,7 +368,7 @@ public EnumerableIterator(Engine engine, IEnumerable target) : base(engine)

public override void Close(CompletionType completion)
{
(_enumerator as IDisposable)?.Dispose();
(_enumerator as IDisposable)?.Dispose();
base.Close(completion);
}

Expand Down
9 changes: 6 additions & 3 deletions Jint/Runtime/Interop/Reflection/MethodAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ namespace Jint.Runtime.Interop.Reflection
{
internal sealed class MethodAccessor : ReflectionAccessor
{
private readonly Type _targetType;
private readonly string _name;
private readonly MethodDescriptor[] _methods;

public MethodAccessor(MethodDescriptor[] methods, string name) : base(null!, null!)
public MethodAccessor(Type targetType, string name, MethodDescriptor[] methods)
: base(null!, name)
{
_methods = methods;
_targetType = targetType;
_name = name;
_methods = methods;
}

public override bool Writable => false;
Expand All @@ -26,7 +29,7 @@ protected override void DoSetValue(object target, object? value)

public override PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target, bool enumerable = true)
{
return new(new MethodInfoFunctionInstance(engine, _methods, _name), PropertyFlag.AllForbidden);
return new(new MethodInfoFunctionInstance(engine, _targetType, _name, _methods), PropertyFlag.AllForbidden);
}
}
}
29 changes: 14 additions & 15 deletions Jint/Runtime/Interop/Reflection/NestedTypeAccessor.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
namespace Jint.Runtime.Interop.Reflection;

namespace Jint.Runtime.Interop.Reflection
internal sealed class NestedTypeAccessor : ReflectionAccessor
{
internal sealed class NestedTypeAccessor : ReflectionAccessor
private readonly TypeReference _typeReference;

public NestedTypeAccessor(TypeReference typeReference, string name) : base(typeof(Type), name)
{
private readonly TypeReference _typeReference;
public NestedTypeAccessor(TypeReference typeReference, string name) : base(typeof(Type), name)
{
_typeReference = typeReference;
}
_typeReference = typeReference;
}

public override bool Writable => false;
public override bool Writable => false;

protected override object? DoGetValue(object target)
{
return _typeReference;
}
protected override object? DoGetValue(object target)
{
return _typeReference;
}

protected override void DoSetValue(object target, object? value)
{
}
protected override void DoSetValue(object target, object? value)
{
}
}
2 changes: 1 addition & 1 deletion Jint/Runtime/Interop/TypeReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ private static JsValue HasInstance(JsValue thisObject, JsValue[] arguments)

public override string ToString()
{
return "[clr type: " + ReferenceType + "]";
return "[CLR type: " + ReferenceType + "]";
}
}
}
6 changes: 3 additions & 3 deletions Jint/Runtime/Interop/TypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ private static IEnumerable<string> NameCreator(MemberInfo info)

if (explicitMethods?.Count > 0)
{
return new MethodAccessor(MethodDescriptor.Build(explicitMethods), memberName);
return new MethodAccessor(type, memberName, MethodDescriptor.Build(explicitMethods));
}

// try to find explicit indexer implementations
Expand Down Expand Up @@ -193,7 +193,7 @@ private static IEnumerable<string> NameCreator(MemberInfo info)

if (matches.Count > 0)
{
return new MethodAccessor(MethodDescriptor.Build(matches), memberName);
return new MethodAccessor(type, memberName, MethodDescriptor.Build(matches));
}
}

Expand Down Expand Up @@ -299,7 +299,7 @@ private static IEnumerable<string> NameCreator(MemberInfo info)

if (methods?.Count > 0)
{
accessor = new MethodAccessor(MethodDescriptor.Build(methods), memberName);
accessor = new MethodAccessor(type, memberName, MethodDescriptor.Build(methods));
return true;
}

Expand Down

0 comments on commit ea0485b

Please sign in to comment.