PropertyDescriptorTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. using Jint.Native;
  2. using Jint.Runtime.Descriptors;
  3. using Jint.Runtime.Descriptors.Specialized;
  4. using Jint.Runtime.Interop;
  5. using Jint.Tests.TestClasses;
  6. namespace Jint.Tests.Runtime;
  7. public class PropertyDescriptorTests
  8. {
  9. public class TestClass
  10. {
  11. public static readonly TestClass Instance = new TestClass();
  12. public string Method() => "Method";
  13. public class NestedType { }
  14. public readonly int fieldReadOnly = 8;
  15. public int field = 42;
  16. public string PropertyReadOnly => "PropertyReadOnly";
  17. public string PropertyWriteOnly { set { } }
  18. public string PropertyReadWrite { get; set; } = "PropertyReadWrite";
  19. public IndexedPropertyReadOnly<int, int> IndexerReadOnly { get; }
  20. = new((idx) => 42);
  21. public IndexedPropertyWriteOnly<int, int> IndexerWriteOnly { get; }
  22. = new((idx, v) => { });
  23. public IndexedProperty<int, int> IndexerReadWrite { get; }
  24. = new((idx) => 42, (idx, v) => { });
  25. }
  26. private readonly Engine _engine;
  27. private readonly bool checkType = true;
  28. public PropertyDescriptorTests()
  29. {
  30. _engine = new Engine(cfg => cfg.AllowClr(
  31. typeof(TestClass).Assembly,
  32. typeof(Console).Assembly,
  33. typeof(File).Assembly))
  34. .SetValue("log", new Action<object>(Console.WriteLine))
  35. .SetValue("assert", new Action<bool>(Assert.True))
  36. .SetValue("equal", new Action<object, object>(Assert.Equal))
  37. .SetValue("testClass", TestClass.Instance)
  38. ;
  39. }
  40. [Fact]
  41. public void PropertyDescriptorReadOnly()
  42. {
  43. var pd = _engine.Evaluate("""
  44. Object.defineProperty({}, 'value', {
  45. value: 42,
  46. writable: false
  47. })
  48. """).AsObject().GetOwnProperty("value");
  49. Assert.Equal(false, pd.IsAccessorDescriptor());
  50. Assert.Equal(true, pd.IsDataDescriptor());
  51. Assert.Equal(false, pd.Writable);
  52. Assert.Null(pd.Get);
  53. Assert.Null(pd.Set);
  54. }
  55. [Fact]
  56. public void PropertyDescriptorReadWrite()
  57. {
  58. var pd = _engine.Evaluate("""
  59. Object.defineProperty({}, 'value', {
  60. value: 42,
  61. writable: true
  62. })
  63. """).AsObject().GetOwnProperty("value");
  64. Assert.Equal(false, pd.IsAccessorDescriptor());
  65. Assert.Equal(true, pd.IsDataDescriptor());
  66. Assert.Equal(true, pd.Writable);
  67. Assert.Null(pd.Get);
  68. Assert.Null(pd.Set);
  69. }
  70. [Fact]
  71. public void UndefinedPropertyDescriptor()
  72. {
  73. var pd = PropertyDescriptor.Undefined;
  74. // PropertyDescriptor.UndefinedPropertyDescriptor is private
  75. //if (checkType) Assert.IsType<PropertyDescriptor.UndefinedPropertyDescriptor>(pd);
  76. Assert.Equal(false, pd.IsAccessorDescriptor());
  77. Assert.Equal(false, pd.IsDataDescriptor());
  78. }
  79. [Fact]
  80. public void AllForbiddenDescriptor()
  81. {
  82. var pd = _engine.Evaluate("Object.getPrototypeOf('s')").AsObject().GetOwnProperty("length");
  83. if (checkType) Assert.IsType<PropertyDescriptor.AllForbiddenDescriptor>(pd);
  84. Assert.Equal(false, pd.IsAccessorDescriptor());
  85. Assert.Equal(true, pd.IsDataDescriptor());
  86. }
  87. [Fact]
  88. public void LazyPropertyDescriptor()
  89. {
  90. var pd = _engine.Evaluate("globalThis").AsObject().GetOwnProperty("decodeURI");
  91. if (checkType) Assert.IsType<LazyPropertyDescriptor>(pd);
  92. Assert.Equal(false, pd.IsAccessorDescriptor());
  93. Assert.Equal(true, pd.IsDataDescriptor());
  94. }
  95. [Fact]
  96. public void ThrowerPropertyDescriptor()
  97. {
  98. var pd = _engine.Evaluate("Object.getPrototypeOf(function() {})").AsObject().GetOwnProperty("arguments");
  99. if (checkType) Assert.IsType<GetSetPropertyDescriptor.ThrowerPropertyDescriptor>(pd);
  100. Assert.Equal(true, pd.IsAccessorDescriptor());
  101. Assert.Equal(false, pd.IsDataDescriptor());
  102. }
  103. [Fact]
  104. public void GetSetPropertyDescriptorGetOnly()
  105. {
  106. var pd = _engine.Evaluate("""
  107. Object.defineProperty({}, 'value', {
  108. get() {}
  109. })
  110. """).AsObject().GetOwnProperty("value");
  111. if (checkType) Assert.IsType<GetSetPropertyDescriptor>(pd);
  112. Assert.Equal(true, pd.IsAccessorDescriptor());
  113. Assert.Equal(false, pd.IsDataDescriptor());
  114. Assert.NotNull(pd.Get);
  115. Assert.Null(pd.Set);
  116. }
  117. [Fact]
  118. public void GetSetPropertyDescriptorSetOnly()
  119. {
  120. var pd = _engine.Evaluate("""
  121. Object.defineProperty({}, 'value', {
  122. set() {}
  123. })
  124. """).AsObject().GetOwnProperty("value");
  125. if (checkType) Assert.IsType<GetSetPropertyDescriptor>(pd);
  126. Assert.Equal(true, pd.IsAccessorDescriptor());
  127. Assert.Equal(false, pd.IsDataDescriptor());
  128. Assert.Null(pd.Get);
  129. Assert.NotNull(pd.Set);
  130. }
  131. [Fact]
  132. public void GetSetPropertyDescriptorGetSet()
  133. {
  134. var pd = _engine.Evaluate("""
  135. Object.defineProperty({}, 'value', {
  136. get() {},
  137. set() {}
  138. })
  139. """).AsObject().GetOwnProperty("value");
  140. if (checkType) Assert.IsType<GetSetPropertyDescriptor>(pd);
  141. Assert.Equal(true, pd.IsAccessorDescriptor());
  142. Assert.Equal(false, pd.IsDataDescriptor());
  143. Assert.NotNull(pd.Get);
  144. Assert.NotNull(pd.Set);
  145. }
  146. [Fact]
  147. public void ClrAccessDescriptor()
  148. {
  149. JsValue ExtractClrAccessDescriptor(JsValue jsArugments)
  150. {
  151. var pd = ((JsArguments) jsArugments).ParameterMap.GetOwnProperty("0");
  152. return new ObjectWrapper(_engine, pd);
  153. }
  154. _engine.SetValue("ExtractClrAccessDescriptor", ExtractClrAccessDescriptor);
  155. var pdobj = _engine.Evaluate("""
  156. (function(a) {
  157. return ExtractClrAccessDescriptor(arguments);
  158. })(42)
  159. """);
  160. var pd = (PropertyDescriptor) ((ObjectWrapper) pdobj).Target;
  161. if (checkType) Assert.IsType<ClrAccessDescriptor>(pd);
  162. Assert.Equal(true, pd.IsAccessorDescriptor());
  163. Assert.Equal(false, pd.IsDataDescriptor());
  164. }
  165. [Fact]
  166. public void PropertyDescriptorMethod()
  167. {
  168. var pdMethod = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'Method')");
  169. CheckPropertyDescriptor(pdMethod, false, false, false, true, false, false);
  170. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("Method");
  171. // use PropertyDescriptor to wrap method directly
  172. //if (checkType) Assert.IsType<PropertyDescriptor>(pd);
  173. Assert.Equal(false, pd.IsAccessorDescriptor());
  174. Assert.Equal(true, pd.IsDataDescriptor());
  175. }
  176. [Fact]
  177. public void PropertyDescriptorNestedType()
  178. {
  179. var pdMethod = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'NestedType')");
  180. CheckPropertyDescriptor(pdMethod, false, false, false, true, false, false);
  181. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("NestedType");
  182. // use PropertyDescriptor to wrap nested type directly
  183. //if (checkType) Assert.IsType<PropertyDescriptor>(pd);
  184. Assert.Equal(false, pd.IsAccessorDescriptor());
  185. Assert.Equal(true, pd.IsDataDescriptor());
  186. }
  187. [Fact]
  188. public void ReflectionDescriptorFieldReadOnly()
  189. {
  190. var pdField = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'fieldReadOnly')");
  191. CheckPropertyDescriptor(pdField, false, true, false, false, true, false);
  192. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("fieldReadOnly");
  193. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  194. Assert.Equal(true, pd.IsAccessorDescriptor());
  195. Assert.Equal(false, pd.IsDataDescriptor());
  196. }
  197. [Fact]
  198. public void ReflectionDescriptorField()
  199. {
  200. var pdField = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'field')");
  201. CheckPropertyDescriptor(pdField, false, true, true, false, true, true);
  202. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("field");
  203. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  204. Assert.Equal(true, pd.IsAccessorDescriptor());
  205. Assert.Equal(false, pd.IsDataDescriptor());
  206. }
  207. [Fact]
  208. public void ReflectionDescriptorPropertyReadOnly()
  209. {
  210. var pdPropertyReadOnly = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'PropertyReadOnly')");
  211. CheckPropertyDescriptor(pdPropertyReadOnly, false, true, false, false, true, false);
  212. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("PropertyReadOnly");
  213. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  214. Assert.Equal(true, pd.IsAccessorDescriptor());
  215. Assert.Equal(false, pd.IsDataDescriptor());
  216. }
  217. [Fact]
  218. public void ReflectionDescriptorPropertyWriteOnly()
  219. {
  220. var pdPropertyWriteOnly = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'PropertyWriteOnly')");
  221. CheckPropertyDescriptor(pdPropertyWriteOnly, false, true, true, false, false, true);
  222. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("PropertyWriteOnly");
  223. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  224. Assert.Equal(true, pd.IsAccessorDescriptor());
  225. Assert.Equal(false, pd.IsDataDescriptor());
  226. }
  227. [Fact]
  228. public void ReflectionDescriptorPropertyReadWrite()
  229. {
  230. var pdPropertyReadWrite = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass, 'PropertyReadWrite')");
  231. CheckPropertyDescriptor(pdPropertyReadWrite, false, true, true, false, true, true);
  232. var pd = _engine.Evaluate("testClass").AsObject().GetOwnProperty("PropertyReadWrite");
  233. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  234. Assert.Equal(true, pd.IsAccessorDescriptor());
  235. Assert.Equal(false, pd.IsDataDescriptor());
  236. }
  237. [Fact]
  238. public void ReflectionDescriptorIndexerReadOnly()
  239. {
  240. var pdIndexerReadOnly = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass.IndexerReadOnly, '1')");
  241. CheckPropertyDescriptor(pdIndexerReadOnly, false, true, false, false, true, false);
  242. var pd1 = _engine.Evaluate("testClass.IndexerReadOnly");
  243. var pd = pd1.AsObject().GetOwnProperty("1");
  244. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  245. Assert.Equal(true, pd.IsAccessorDescriptor());
  246. Assert.Equal(false, pd.IsDataDescriptor());
  247. }
  248. [Fact]
  249. public void ReflectionDescriptorIndexerWriteOnly()
  250. {
  251. var pdIndexerWriteOnly = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass.IndexerWriteOnly, '1')");
  252. CheckPropertyDescriptor(pdIndexerWriteOnly, false, true, true, false, false, true);
  253. var pd = _engine.Evaluate("testClass.IndexerWriteOnly").AsObject().GetOwnProperty("1");
  254. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  255. Assert.Equal(true, pd.IsAccessorDescriptor());
  256. Assert.Equal(false, pd.IsDataDescriptor());
  257. }
  258. [Fact]
  259. public void ReflectionDescriptorIndexerReadWrite()
  260. {
  261. var pdIndexerReadWrite = _engine.Evaluate("Object.getOwnPropertyDescriptor(testClass.IndexerReadWrite, 1)");
  262. CheckPropertyDescriptor(pdIndexerReadWrite, false, true, true, false, true, true);
  263. var pd = _engine.Evaluate("testClass.IndexerReadWrite").AsObject().GetOwnProperty("1");
  264. if (checkType) Assert.IsType<ReflectionDescriptor>(pd);
  265. Assert.Equal(true, pd.IsAccessorDescriptor());
  266. Assert.Equal(false, pd.IsDataDescriptor());
  267. }
  268. private void CheckPropertyDescriptor(
  269. JsValue jsPropertyDescriptor,
  270. bool configurable,
  271. bool enumerable,
  272. bool writable,
  273. bool hasValue,
  274. bool hasGet,
  275. bool hasSet
  276. )
  277. {
  278. var pd = jsPropertyDescriptor.AsObject();
  279. Assert.Equal(configurable, pd["configurable"].AsBoolean());
  280. Assert.Equal(enumerable, pd["enumerable"].AsBoolean());
  281. if (writable)
  282. {
  283. var writableActual = pd["writable"];
  284. if (!writableActual.IsUndefined())
  285. {
  286. Assert.True(writableActual.AsBoolean());
  287. }
  288. }
  289. Assert.Equal(hasValue, !pd["value"].IsUndefined());
  290. Assert.Equal(hasGet, !pd["get"].IsUndefined());
  291. Assert.Equal(hasSet, !pd["set"].IsUndefined());
  292. }
  293. [Fact]
  294. public void DefinePropertyFromAccesorToData()
  295. {
  296. var pd = _engine.Evaluate("""
  297. let o = {};
  298. Object.defineProperty(o, 'foo', {
  299. get() { return 1; },
  300. configurable: true
  301. });
  302. Object.defineProperty(o, 'foo', {
  303. value: 101
  304. });
  305. return Object.getOwnPropertyDescriptor(o, 'foo');
  306. """);
  307. Assert.Equal(101, pd.AsObject().Get("value").AsInteger());
  308. CheckPropertyDescriptor(pd, true, false, false, true, false, false);
  309. }
  310. }