PropertyDescriptor.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. using System.Runtime.CompilerServices;
  2. using Jint.Native;
  3. using Jint.Native.Object;
  4. using Jint.Runtime.Descriptors.Specialized;
  5. namespace Jint.Runtime.Descriptors
  6. {
  7. public class PropertyDescriptor
  8. {
  9. public static readonly PropertyDescriptor Undefined = new PropertyDescriptor(PropertyFlag.None);
  10. internal PropertyFlag _flags;
  11. internal JsValue _value;
  12. protected PropertyDescriptor(PropertyFlag flags)
  13. {
  14. _flags = flags;
  15. }
  16. protected internal PropertyDescriptor(JsValue value, PropertyFlag flags) : this(flags)
  17. {
  18. if ((_flags & PropertyFlag.CustomJsValue) != 0)
  19. {
  20. CustomValue = value;
  21. }
  22. _value = value;
  23. }
  24. public PropertyDescriptor(JsValue value, bool? writable, bool? enumerable, bool? configurable)
  25. {
  26. if ((_flags & PropertyFlag.CustomJsValue) != 0)
  27. {
  28. CustomValue = value;
  29. }
  30. _value = value;
  31. if (writable != null)
  32. {
  33. Writable = writable.Value;
  34. WritableSet = true;
  35. }
  36. if (enumerable != null)
  37. {
  38. Enumerable = enumerable.Value;
  39. EnumerableSet = true;
  40. }
  41. if (configurable != null)
  42. {
  43. Configurable = configurable.Value;
  44. ConfigurableSet = true;
  45. }
  46. }
  47. public PropertyDescriptor(PropertyDescriptor descriptor)
  48. {
  49. Value = descriptor.Value;
  50. Enumerable = descriptor.Enumerable;
  51. EnumerableSet = descriptor.EnumerableSet;
  52. Configurable = descriptor.Configurable;
  53. ConfigurableSet = descriptor.ConfigurableSet;
  54. Writable = descriptor.Writable;
  55. WritableSet = descriptor.WritableSet;
  56. }
  57. public virtual JsValue Get => null;
  58. public virtual JsValue Set => null;
  59. public bool Enumerable
  60. {
  61. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  62. get => (_flags & PropertyFlag.Enumerable) != 0;
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. set
  65. {
  66. _flags |= PropertyFlag.EnumerableSet;
  67. if (value)
  68. {
  69. _flags |= PropertyFlag.Enumerable;
  70. }
  71. else
  72. {
  73. _flags &= ~(PropertyFlag.Enumerable);
  74. }
  75. }
  76. }
  77. public bool EnumerableSet
  78. {
  79. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  80. get => (_flags & (PropertyFlag.EnumerableSet | PropertyFlag.Enumerable)) != 0;
  81. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  82. private set
  83. {
  84. if (value)
  85. {
  86. _flags |= PropertyFlag.EnumerableSet;
  87. }
  88. else
  89. {
  90. _flags &= ~(PropertyFlag.EnumerableSet);
  91. }
  92. }
  93. }
  94. public bool Writable
  95. {
  96. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  97. get => (_flags & PropertyFlag.Writable) != 0;
  98. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  99. set
  100. {
  101. _flags |= PropertyFlag.WritableSet;
  102. if (value)
  103. {
  104. _flags |= PropertyFlag.Writable;
  105. }
  106. else
  107. {
  108. _flags &= ~(PropertyFlag.Writable);
  109. }
  110. }
  111. }
  112. public bool WritableSet
  113. {
  114. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  115. get => (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0;
  116. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  117. private set
  118. {
  119. if (value)
  120. {
  121. _flags |= PropertyFlag.WritableSet;
  122. }
  123. else
  124. {
  125. _flags &= ~(PropertyFlag.WritableSet);
  126. }
  127. }
  128. }
  129. public bool Configurable
  130. {
  131. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  132. get => (_flags & PropertyFlag.Configurable) != 0;
  133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  134. set
  135. {
  136. _flags |= PropertyFlag.ConfigurableSet;
  137. if (value)
  138. {
  139. _flags |= PropertyFlag.Configurable;
  140. }
  141. else
  142. {
  143. _flags &= ~(PropertyFlag.Configurable);
  144. }
  145. }
  146. }
  147. public bool ConfigurableSet
  148. {
  149. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  150. get => (_flags & (PropertyFlag.ConfigurableSet | PropertyFlag.Configurable)) != 0;
  151. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  152. private set
  153. {
  154. if (value)
  155. {
  156. _flags |= PropertyFlag.ConfigurableSet;
  157. }
  158. else
  159. {
  160. _flags &= ~(PropertyFlag.ConfigurableSet);
  161. }
  162. }
  163. }
  164. public JsValue Value
  165. {
  166. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  167. get
  168. {
  169. if ((_flags & PropertyFlag.CustomJsValue) != 0)
  170. {
  171. return CustomValue;
  172. }
  173. return _value;
  174. }
  175. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  176. set
  177. {
  178. if ((_flags & PropertyFlag.CustomJsValue) != 0)
  179. {
  180. CustomValue = value;
  181. }
  182. _value = value;
  183. }
  184. }
  185. protected virtual JsValue CustomValue
  186. {
  187. get => null;
  188. set => ExceptionHelper.ThrowNotImplementedException();
  189. }
  190. internal PropertyFlag Flags
  191. {
  192. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  193. get { return _flags; }
  194. }
  195. public static PropertyDescriptor ToPropertyDescriptor(Engine engine, JsValue o)
  196. {
  197. var obj = o.TryCast<ObjectInstance>();
  198. if (ReferenceEquals(obj, null))
  199. {
  200. ExceptionHelper.ThrowTypeError(engine);
  201. }
  202. var getProperty = obj.GetProperty("get");
  203. var hasGetProperty = getProperty != Undefined;
  204. var setProperty = obj.GetProperty("set");
  205. var hasSetProperty = setProperty != Undefined;
  206. if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
  207. (hasGetProperty || hasSetProperty))
  208. {
  209. ExceptionHelper.ThrowTypeError(engine);
  210. }
  211. var desc = hasGetProperty || hasSetProperty
  212. ? new GetSetPropertyDescriptor(null, null, PropertyFlag.None)
  213. : new PropertyDescriptor(PropertyFlag.None);
  214. var enumerableProperty = obj.GetProperty("enumerable");
  215. if (enumerableProperty != Undefined)
  216. {
  217. desc.Enumerable = TypeConverter.ToBoolean(obj.UnwrapJsValue(enumerableProperty));
  218. desc.EnumerableSet = true;
  219. }
  220. var configurableProperty = obj.GetProperty("configurable");
  221. if (configurableProperty != Undefined)
  222. {
  223. desc.Configurable = TypeConverter.ToBoolean(obj.UnwrapJsValue(configurableProperty));
  224. desc.ConfigurableSet = true;
  225. }
  226. var valueProperty = obj.GetProperty("value");
  227. if (valueProperty != Undefined)
  228. {
  229. desc.Value = obj.UnwrapJsValue(valueProperty);
  230. }
  231. var writableProperty = obj.GetProperty("writable");
  232. if (writableProperty != Undefined)
  233. {
  234. desc.Writable = TypeConverter.ToBoolean(obj.UnwrapJsValue(writableProperty));
  235. desc.WritableSet = true;
  236. }
  237. if (hasGetProperty)
  238. {
  239. var getter = obj.UnwrapJsValue(getProperty);
  240. if (!getter.IsUndefined() && getter.TryCast<ICallable>() == null)
  241. {
  242. ExceptionHelper.ThrowTypeError(engine);
  243. }
  244. ((GetSetPropertyDescriptor) desc).SetGet(getter);
  245. }
  246. if (hasSetProperty)
  247. {
  248. var setter = obj.UnwrapJsValue(setProperty);
  249. if (!setter.IsUndefined() && setter.TryCast<ICallable>() == null)
  250. {
  251. ExceptionHelper.ThrowTypeError(engine);
  252. }
  253. ((GetSetPropertyDescriptor) desc).SetSet(setter);
  254. }
  255. if (!ReferenceEquals(desc.Get, null))
  256. {
  257. if (!ReferenceEquals(desc.Value, null) || desc.WritableSet)
  258. {
  259. ExceptionHelper.ThrowTypeError(engine);
  260. }
  261. }
  262. return desc;
  263. }
  264. public static JsValue FromPropertyDescriptor(Engine engine, PropertyDescriptor desc)
  265. {
  266. if (ReferenceEquals(desc, Undefined))
  267. {
  268. return Native.Undefined.Instance;
  269. }
  270. var obj = engine.Object.Construct(Arguments.Empty);
  271. if (desc.IsDataDescriptor())
  272. {
  273. obj.SetOwnProperty("value", new PropertyDescriptor(desc.Value ?? Native.Undefined.Instance, PropertyFlag.ConfigurableEnumerableWritable));
  274. obj.SetOwnProperty("writable", new PropertyDescriptor(desc.Writable, PropertyFlag.ConfigurableEnumerableWritable));
  275. }
  276. else
  277. {
  278. obj.SetOwnProperty("get", new PropertyDescriptor(desc.Get ?? Native.Undefined.Instance, PropertyFlag.ConfigurableEnumerableWritable));
  279. obj.SetOwnProperty("set", new PropertyDescriptor(desc.Set ?? Native.Undefined.Instance, PropertyFlag.ConfigurableEnumerableWritable));
  280. }
  281. obj.SetOwnProperty("enumerable", new PropertyDescriptor(desc.Enumerable, PropertyFlag.ConfigurableEnumerableWritable));
  282. obj.SetOwnProperty("configurable", new PropertyDescriptor(desc.Configurable, PropertyFlag.ConfigurableEnumerableWritable));
  283. return obj;
  284. }
  285. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  286. public bool IsAccessorDescriptor()
  287. {
  288. return !ReferenceEquals(Get, null) || !ReferenceEquals(Set, null);
  289. }
  290. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  291. public bool IsDataDescriptor()
  292. {
  293. return (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0
  294. || (_flags & PropertyFlag.CustomJsValue) != 0 && !ReferenceEquals(CustomValue, null)
  295. || !ReferenceEquals(_value, null);
  296. }
  297. /// <summary>
  298. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3
  299. /// </summary>
  300. /// <returns></returns>
  301. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  302. public bool IsGenericDescriptor()
  303. {
  304. return !IsDataDescriptor() && !IsAccessorDescriptor();
  305. }
  306. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  307. internal bool TryGetValue(ObjectInstance thisArg, out JsValue value)
  308. {
  309. value = JsValue.Undefined;
  310. // IsDataDescriptor logic inlined
  311. if ((_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0)
  312. {
  313. var val = (_flags & PropertyFlag.CustomJsValue) != 0
  314. ? CustomValue
  315. : _value;
  316. if (!ReferenceEquals(val, null))
  317. {
  318. value = val;
  319. return true;
  320. }
  321. }
  322. if (this == Undefined)
  323. {
  324. return false;
  325. }
  326. var getter = Get;
  327. if (!ReferenceEquals(getter, null) && !getter.IsUndefined())
  328. {
  329. // if getter is not undefined it must be ICallable
  330. var callable = getter.TryCast<ICallable>();
  331. value = callable.Call(thisArg, Arguments.Empty);
  332. }
  333. return true;
  334. }
  335. }
  336. }