PropertyDescriptor.cs 14 KB

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