2
0

TypeReference.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using Jint.Collections;
  6. using Jint.Native;
  7. using Jint.Native.Function;
  8. using Jint.Native.Object;
  9. using Jint.Runtime.Descriptors;
  10. using Jint.Runtime.Interop.Reflection;
  11. namespace Jint.Runtime.Interop
  12. {
  13. public sealed class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
  14. {
  15. private static readonly JsString _name = new JsString("typereference");
  16. private static readonly ConcurrentDictionary<Type, MethodDescriptor[]> _constructorCache = new();
  17. private static readonly ConcurrentDictionary<Tuple<Type, string>, ReflectionAccessor> _memberAccessors = new();
  18. private TypeReference(
  19. Engine engine,
  20. Realm realm)
  21. : base(engine, realm, _name, FunctionThisMode.Global, ObjectClass.TypeReference)
  22. {
  23. }
  24. public Type ReferenceType { get; set; }
  25. public static TypeReference CreateTypeReference(Engine engine, Type type)
  26. {
  27. var obj = new TypeReference(engine, engine.Realm);
  28. obj.PreventExtensions();
  29. obj.ReferenceType = type;
  30. // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
  31. obj._prototype = engine.Realm.Intrinsics.Function.PrototypeObject;
  32. obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
  33. // The initial value of Boolean.prototype is the Boolean prototype object
  34. obj._prototypeDescriptor = new PropertyDescriptor(engine.Realm.Intrinsics.Object.PrototypeObject, PropertyFlag.AllForbidden);
  35. return obj;
  36. }
  37. public override JsValue Call(JsValue thisObject, JsValue[] arguments)
  38. {
  39. // direct calls on a TypeReference constructor object is equivalent to the new operator
  40. return Construct(arguments, thisObject);
  41. }
  42. public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
  43. {
  44. if (arguments.Length == 0 && ReferenceType.IsValueType)
  45. {
  46. var instance = Activator.CreateInstance(ReferenceType);
  47. var result = TypeConverter.ToObject(_realm, FromObject(Engine, instance));
  48. return result;
  49. }
  50. var constructors = _constructorCache.GetOrAdd(
  51. ReferenceType,
  52. t => MethodDescriptor.Build(t.GetConstructors(BindingFlags.Public | BindingFlags.Instance)));
  53. foreach (var tuple in TypeConverter.FindBestMatch(_engine, constructors, _ => arguments))
  54. {
  55. var method = tuple.Item1;
  56. var retVal = method.Call(Engine, null, arguments);
  57. var result = TypeConverter.ToObject(_realm, retVal);
  58. // todo: cache method info
  59. return result;
  60. }
  61. ExceptionHelper.ThrowTypeError(_engine.Realm, "No public methods with the specified arguments were found.");
  62. return null;
  63. }
  64. internal override bool OrdinaryHasInstance(JsValue v)
  65. {
  66. if (v is IObjectWrapper wrapper)
  67. {
  68. return wrapper.Target.GetType() == ReferenceType;
  69. }
  70. return base.OrdinaryHasInstance(v);
  71. }
  72. public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  73. {
  74. return false;
  75. }
  76. public override bool Delete(JsValue property)
  77. {
  78. return false;
  79. }
  80. public override bool Set(JsValue property, JsValue value, JsValue receiver)
  81. {
  82. if (!CanPut(property))
  83. {
  84. return false;
  85. }
  86. var ownDesc = GetOwnProperty(property);
  87. if (ownDesc == null)
  88. {
  89. return false;
  90. }
  91. ownDesc.Value = value;
  92. return true;
  93. }
  94. public override PropertyDescriptor GetOwnProperty(JsValue property)
  95. {
  96. if (property is not JsString jsString)
  97. {
  98. return PropertyDescriptor.Undefined;
  99. }
  100. var key = jsString._value;
  101. var descriptor = PropertyDescriptor.Undefined;
  102. if (_properties?.TryGetValue(key, out descriptor) != true)
  103. {
  104. descriptor = CreatePropertyDescriptor(key);
  105. _properties ??= new PropertyDictionary();
  106. _properties[key] = descriptor;
  107. }
  108. return descriptor;
  109. }
  110. private PropertyDescriptor CreatePropertyDescriptor(string name)
  111. {
  112. var accessor = _memberAccessors.GetOrAdd(
  113. new Tuple<Type, string>(ReferenceType, name),
  114. key => ResolveMemberAccessor(key.Item1, key.Item2)
  115. );
  116. return accessor.CreatePropertyDescriptor(_engine, ReferenceType);
  117. }
  118. private static ReflectionAccessor ResolveMemberAccessor(Type type, string name)
  119. {
  120. if (type.IsEnum)
  121. {
  122. var enumValues = Enum.GetValues(type);
  123. var enumNames = Enum.GetNames(type);
  124. for (var i = 0; i < enumValues.Length; i++)
  125. {
  126. if (enumNames.GetValue(i) as string == name)
  127. {
  128. var value = enumValues.GetValue(i);
  129. return new ConstantValueAccessor(JsNumber.Create(value));
  130. }
  131. }
  132. return ConstantValueAccessor.NullAccessor;
  133. }
  134. var propertyInfo = type.GetProperty(name, BindingFlags.Public | BindingFlags.Static);
  135. if (propertyInfo != null)
  136. {
  137. return new PropertyAccessor(name, propertyInfo);
  138. }
  139. var fieldInfo = type.GetField(name, BindingFlags.Public | BindingFlags.Static);
  140. if (fieldInfo != null)
  141. {
  142. return new FieldAccessor(fieldInfo, name);
  143. }
  144. List<MethodInfo> methods = null;
  145. foreach (var mi in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
  146. {
  147. if (mi.Name != name)
  148. {
  149. continue;
  150. }
  151. methods ??= new List<MethodInfo>();
  152. methods.Add(mi);
  153. }
  154. if (methods == null || methods.Count == 0)
  155. {
  156. return ConstantValueAccessor.NullAccessor;
  157. }
  158. return new MethodAccessor(MethodDescriptor.Build(methods));
  159. }
  160. public object Target => ReferenceType;
  161. }
  162. }