TypeReference.cs 7.9 KB

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