TypeReference.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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
  81. {
  82. // ignore method
  83. }
  84. }
  85. return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine, "No public methods with the specified arguments were found.");
  86. }
  87. public override bool HasInstance(JsValue v)
  88. {
  89. if (v.IsObject())
  90. {
  91. var wrapper = v.AsObject() as IObjectWrapper;
  92. if (wrapper != null)
  93. return wrapper.Target.GetType() == ReferenceType;
  94. }
  95. return base.HasInstance(v);
  96. }
  97. public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  98. {
  99. return false;
  100. }
  101. public override bool Delete(JsValue property)
  102. {
  103. return false;
  104. }
  105. public override bool Set(JsValue property, JsValue value, JsValue receiver)
  106. {
  107. if (!CanPut(property))
  108. {
  109. return false;
  110. }
  111. var ownDesc = GetOwnProperty(property);
  112. if (ownDesc == null)
  113. {
  114. return false;
  115. }
  116. ownDesc.Value = value;
  117. return true;
  118. }
  119. public override PropertyDescriptor GetOwnProperty(JsValue property)
  120. {
  121. if (property is not JsString jsString)
  122. {
  123. return PropertyDescriptor.Undefined;
  124. }
  125. var key = jsString._value;
  126. var descriptor = PropertyDescriptor.Undefined;
  127. if (_properties?.TryGetValue(key, out descriptor) != true)
  128. {
  129. descriptor = CreatePropertyDescriptor(key);
  130. _properties ??= new PropertyDictionary();
  131. _properties[key] = descriptor;
  132. }
  133. return descriptor;
  134. }
  135. private PropertyDescriptor CreatePropertyDescriptor(string name)
  136. {
  137. var accessor = _memberAccessors.GetOrAdd(
  138. new Tuple<Type, string>(ReferenceType, name),
  139. key => ResolveMemberAccessor(key.Item1, key.Item2)
  140. );
  141. return accessor.CreatePropertyDescriptor(_engine, ReferenceType);
  142. }
  143. private static ReflectionAccessor ResolveMemberAccessor(Type type, string name)
  144. {
  145. if (type.IsEnum)
  146. {
  147. var enumValues = Enum.GetValues(type);
  148. var enumNames = Enum.GetNames(type);
  149. for (var i = 0; i < enumValues.Length; i++)
  150. {
  151. if (enumNames.GetValue(i) as string == name)
  152. {
  153. return new ConstantValueAccessor((int) enumValues.GetValue(i));
  154. }
  155. }
  156. return ConstantValueAccessor.NullAccessor;
  157. }
  158. var propertyInfo = type.GetProperty(name, BindingFlags.Public | BindingFlags.Static);
  159. if (propertyInfo != null)
  160. {
  161. return new PropertyAccessor(name, propertyInfo);
  162. }
  163. var fieldInfo = type.GetField(name, BindingFlags.Public | BindingFlags.Static);
  164. if (fieldInfo != null)
  165. {
  166. return new FieldAccessor(fieldInfo, name);
  167. }
  168. List<MethodInfo> methods = null;
  169. foreach (var mi in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
  170. {
  171. if (mi.Name != name)
  172. {
  173. continue;
  174. }
  175. methods ??= new List<MethodInfo>();
  176. methods.Add(mi);
  177. }
  178. if (methods == null || methods.Count == 0)
  179. {
  180. return ConstantValueAccessor.NullAccessor;
  181. }
  182. return new MethodAccessor(MethodDescriptor.Build(methods));
  183. }
  184. public object Target => ReferenceType;
  185. }
  186. }