PropertyDescriptorTests.cs 13 KB

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