ObjectInstance.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. using System.Collections.Generic;
  2. using Jint.Runtime;
  3. using Jint.Runtime.Descriptors;
  4. using System;
  5. namespace Jint.Native.Object
  6. {
  7. public class ObjectInstance
  8. {
  9. public ObjectInstance(Engine engine)
  10. {
  11. Engine = engine;
  12. Properties = new MruPropertyCache2<string, PropertyDescriptor>();
  13. }
  14. public Engine Engine { get; set; }
  15. protected IDictionary<string, PropertyDescriptor> Properties { get; private set; }
  16. /// <summary>
  17. /// The prototype of this object.
  18. /// </summary>
  19. public ObjectInstance Prototype { get; set; }
  20. /// <summary>
  21. /// If true, own properties may be added to the
  22. /// object.
  23. /// </summary>
  24. public bool Extensible { get; set; }
  25. /// <summary>
  26. /// A String value indicating a specification defined
  27. /// classification of objects.
  28. /// </summary>
  29. public virtual string Class
  30. {
  31. get { return "Object"; }
  32. }
  33. public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
  34. {
  35. return Properties;
  36. }
  37. public virtual bool HasOwnProperty(string p)
  38. {
  39. return Properties.ContainsKey(p);
  40. }
  41. public virtual void RemoveOwnProperty(string p)
  42. {
  43. Properties.Remove(p);
  44. }
  45. /// <summary>
  46. /// Returns the value of the named property.
  47. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.3
  48. /// </summary>
  49. /// <param name="propertyName"></param>
  50. /// <returns></returns>
  51. public virtual JsValue Get(string propertyName)
  52. {
  53. var desc = GetProperty(propertyName);
  54. if (desc == PropertyDescriptor.Undefined)
  55. {
  56. return JsValue.Undefined;
  57. }
  58. if (desc.IsDataDescriptor())
  59. {
  60. return desc.Value.HasValue ? desc.Value.Value : Undefined.Instance;
  61. }
  62. var getter = desc.Get.HasValue ? desc.Get.Value : Undefined.Instance;
  63. if (getter.IsUndefined())
  64. {
  65. return Undefined.Instance;
  66. }
  67. // if getter is not undefined it must be ICallable
  68. var callable = getter.TryCast<ICallable>();
  69. return callable.Call(this, Arguments.Empty);
  70. }
  71. /// <summary>
  72. /// Returns the Property Descriptor of the named
  73. /// own property of this object, or undefined if
  74. /// absent.
  75. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.1
  76. /// </summary>
  77. /// <param name="propertyName"></param>
  78. /// <returns></returns>
  79. public virtual PropertyDescriptor GetOwnProperty(string propertyName)
  80. {
  81. PropertyDescriptor x;
  82. if (Properties.TryGetValue(propertyName, out x))
  83. {
  84. /* Spec implementation
  85. PropertyDescriptor d;
  86. if (x.IsDataDescriptor())
  87. {
  88. d = new PropertyDescriptor(x.As<DataDescriptor>());
  89. }
  90. else
  91. {
  92. d = new PropertyDescriptor(x.As<AccessorDescriptor>());
  93. }
  94. return d;
  95. */
  96. // optimmized implementation
  97. return x;
  98. }
  99. return PropertyDescriptor.Undefined;
  100. }
  101. protected virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
  102. {
  103. Properties[propertyName] = desc;
  104. }
  105. /// <summary>
  106. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.2
  107. /// </summary>
  108. /// <param name="propertyName"></param>
  109. /// <returns></returns>
  110. public PropertyDescriptor GetProperty(string propertyName)
  111. {
  112. var prop = GetOwnProperty(propertyName);
  113. if (prop != PropertyDescriptor.Undefined)
  114. {
  115. return prop;
  116. }
  117. if(Prototype == null)
  118. {
  119. return PropertyDescriptor.Undefined;
  120. }
  121. return Prototype.GetProperty(propertyName);
  122. }
  123. /// <summary>
  124. /// Sets the specified named property to the value
  125. /// of the second parameter. The flag controls
  126. /// failure handling.
  127. /// </summary>
  128. /// <param name="propertyName"></param>
  129. /// <param name="value"></param>
  130. /// <param name="throwOnError"></param>
  131. public virtual void Put(string propertyName, JsValue value, bool throwOnError)
  132. {
  133. if (!CanPut(propertyName))
  134. {
  135. if (throwOnError)
  136. {
  137. throw new JavaScriptException(Engine.TypeError);
  138. }
  139. return;
  140. }
  141. var ownDesc = GetOwnProperty(propertyName);
  142. if (ownDesc.IsDataDescriptor())
  143. {
  144. ownDesc.Value = value;
  145. return;
  146. // as per specification
  147. // var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
  148. // DefineOwnProperty(propertyName, valueDesc, throwOnError);
  149. // return;
  150. }
  151. // property is an accessor or inherited
  152. var desc = GetProperty(propertyName);
  153. if (desc.IsAccessorDescriptor())
  154. {
  155. var setter = desc.Set.Value.TryCast<ICallable>();
  156. setter.Call(new JsValue(this), new [] {value});
  157. }
  158. else
  159. {
  160. var newDesc = new PropertyDescriptor(value, true, true, true);
  161. DefineOwnProperty(propertyName, newDesc, throwOnError);
  162. }
  163. }
  164. /// <summary>
  165. /// Returns a Boolean value indicating whether a
  166. /// [[Put]] operation with PropertyName can be
  167. /// performed.
  168. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.4
  169. /// </summary>
  170. /// <param name="propertyName"></param>
  171. /// <returns></returns>
  172. public bool CanPut(string propertyName)
  173. {
  174. var desc = GetOwnProperty(propertyName);
  175. if (desc != PropertyDescriptor.Undefined)
  176. {
  177. if (desc.IsAccessorDescriptor())
  178. {
  179. if (!desc.Set.HasValue || desc.Set.Value.IsUndefined())
  180. {
  181. return false;
  182. }
  183. return true;
  184. }
  185. return desc.Writable.HasValue && desc.Writable.Value;
  186. }
  187. if (Prototype == null)
  188. {
  189. return Extensible;
  190. }
  191. var inherited = Prototype.GetProperty(propertyName);
  192. if (inherited == PropertyDescriptor.Undefined)
  193. {
  194. return Extensible;
  195. }
  196. if (inherited.IsAccessorDescriptor())
  197. {
  198. if (!inherited.Set.HasValue || inherited.Set.Value.IsUndefined())
  199. {
  200. return false;
  201. }
  202. return true;
  203. }
  204. if (!Extensible)
  205. {
  206. return false;
  207. }
  208. else
  209. {
  210. return inherited.Writable.HasValue && inherited.Writable.Value;
  211. }
  212. }
  213. /// <summary>
  214. /// Returns a Boolean value indicating whether the
  215. /// object already has a property with the given
  216. /// name.
  217. /// </summary>
  218. /// <param name="propertyName"></param>
  219. /// <returns></returns>
  220. public bool HasProperty(string propertyName)
  221. {
  222. return GetProperty(propertyName) != PropertyDescriptor.Undefined;
  223. }
  224. /// <summary>
  225. /// Removes the specified named own property
  226. /// from the object. The flag controls failure
  227. /// handling.
  228. /// </summary>
  229. /// <param name="propertyName"></param>
  230. /// <param name="throwOnError"></param>
  231. /// <returns></returns>
  232. public virtual bool Delete(string propertyName, bool throwOnError)
  233. {
  234. var desc = GetOwnProperty(propertyName);
  235. if (desc == PropertyDescriptor.Undefined)
  236. {
  237. return true;
  238. }
  239. if (desc.Configurable.HasValue && desc.Configurable.Value)
  240. {
  241. RemoveOwnProperty(propertyName);
  242. return true;
  243. }
  244. else
  245. {
  246. if (throwOnError)
  247. {
  248. throw new JavaScriptException(Engine.TypeError);
  249. }
  250. return false;
  251. }
  252. }
  253. /// <summary>
  254. /// Hint is a String. Returns a default value for the
  255. /// object.
  256. /// </summary>
  257. /// <param name="hint"></param>
  258. /// <returns></returns>
  259. public JsValue DefaultValue(Types hint)
  260. {
  261. if (hint == Types.String || (hint == Types.None && Class == "Date"))
  262. {
  263. var toString = Get("toString").TryCast<ICallable>();
  264. if (toString != null)
  265. {
  266. var str = toString.Call(new JsValue(this), Arguments.Empty);
  267. if (str.IsPrimitive())
  268. {
  269. return str;
  270. }
  271. }
  272. var valueOf = Get("valueOf").TryCast<ICallable>();
  273. if (valueOf != null)
  274. {
  275. var val = valueOf.Call(new JsValue(this), Arguments.Empty);
  276. if (val.IsPrimitive())
  277. {
  278. return val;
  279. }
  280. }
  281. throw new JavaScriptException(Engine.TypeError);
  282. }
  283. if (hint == Types.Number || hint == Types.None)
  284. {
  285. var valueOf = Get("valueOf").TryCast<ICallable>();
  286. if (valueOf != null)
  287. {
  288. var val = valueOf.Call(new JsValue(this), Arguments.Empty);
  289. if (val.IsPrimitive())
  290. {
  291. return val;
  292. }
  293. }
  294. var toString = Get("toString").TryCast<ICallable>();
  295. if (toString != null)
  296. {
  297. var str = toString.Call(new JsValue(this), Arguments.Empty);
  298. if (str.IsPrimitive())
  299. {
  300. return str;
  301. }
  302. }
  303. throw new JavaScriptException(Engine.TypeError);
  304. }
  305. return ToString();
  306. }
  307. /// <summary>
  308. /// Creates or alters the named own property to
  309. /// have the state described by a Property
  310. /// Descriptor. The flag controls failure handling.
  311. /// </summary>
  312. /// <param name="propertyName"></param>
  313. /// <param name="desc"></param>
  314. /// <param name="throwOnError"></param>
  315. /// <returns></returns>
  316. public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
  317. {
  318. var current = GetOwnProperty(propertyName);
  319. if (current == desc) {
  320. return true;
  321. }
  322. if (current == PropertyDescriptor.Undefined)
  323. {
  324. if (!Extensible)
  325. {
  326. if (throwOnError)
  327. {
  328. throw new JavaScriptException(Engine.TypeError);
  329. }
  330. return false;
  331. }
  332. else
  333. {
  334. if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
  335. {
  336. SetOwnProperty(propertyName, new PropertyDescriptor(desc)
  337. {
  338. Value = desc.Value.HasValue ? desc.Value : JsValue.Undefined,
  339. Writable = desc.Writable.HasValue ? desc.Writable.Value : false,
  340. Enumerable = desc.Enumerable.HasValue ? desc.Enumerable.Value : false,
  341. Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false
  342. });
  343. }
  344. else
  345. {
  346. SetOwnProperty(propertyName, new PropertyDescriptor(desc)
  347. {
  348. Get = desc.Get,
  349. Set = desc.Set,
  350. Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false,
  351. Configurable = desc.Configurable.HasValue ? desc.Configurable : false,
  352. });
  353. }
  354. }
  355. return true;
  356. }
  357. // Step 5
  358. if (!current.Configurable.HasValue &&
  359. !current.Enumerable.HasValue &&
  360. !current.Writable.HasValue &&
  361. !current.Get.HasValue &&
  362. !current.Set.HasValue &&
  363. !current.Value.HasValue)
  364. {
  365. return true;
  366. }
  367. // Step 6
  368. if (
  369. current.Configurable == desc.Configurable &&
  370. current.Writable == desc.Writable &&
  371. current.Enumerable == desc.Enumerable &&
  372. ((!current.Get.HasValue && !desc.Get.HasValue) || (current.Get.HasValue && desc.Get.HasValue && ExpressionInterpreter.SameValue(current.Get.Value, desc.Get.Value))) &&
  373. ((!current.Set.HasValue && !desc.Set.HasValue) || (current.Set.HasValue && desc.Set.HasValue && ExpressionInterpreter.SameValue(current.Set.Value, desc.Set.Value))) &&
  374. ((!current.Value.HasValue && !desc.Value.HasValue) || (current.Value.HasValue && desc.Value.HasValue && ExpressionInterpreter.StrictlyEqual(current.Value.Value, desc.Value.Value)))
  375. ) {
  376. return true;
  377. }
  378. if (!current.Configurable.HasValue || !current.Configurable.Value)
  379. {
  380. if (desc.Configurable.HasValue && desc.Configurable.Value)
  381. {
  382. if (throwOnError)
  383. {
  384. throw new JavaScriptException(Engine.TypeError);
  385. }
  386. return false;
  387. }
  388. if (desc.Enumerable.HasValue && (!current.Enumerable.HasValue || desc.Enumerable.Value != current.Enumerable.Value))
  389. {
  390. if (throwOnError)
  391. {
  392. throw new JavaScriptException(Engine.TypeError);
  393. }
  394. return false;
  395. }
  396. }
  397. if (!desc.IsGenericDescriptor())
  398. {
  399. if (current.IsDataDescriptor() != desc.IsDataDescriptor())
  400. {
  401. if (!current.Configurable.HasValue || !current.Configurable.Value)
  402. {
  403. if (throwOnError)
  404. {
  405. throw new JavaScriptException(Engine.TypeError);
  406. }
  407. return false;
  408. }
  409. if (current.IsDataDescriptor())
  410. {
  411. SetOwnProperty(propertyName, current = new PropertyDescriptor(
  412. get: Undefined.Instance,
  413. set: Undefined.Instance,
  414. enumerable: current.Enumerable,
  415. configurable: current.Configurable
  416. ));
  417. }
  418. else
  419. {
  420. SetOwnProperty(propertyName, current = new PropertyDescriptor(
  421. value: Undefined.Instance,
  422. writable: null,
  423. enumerable: current.Enumerable,
  424. configurable: current.Configurable
  425. ));
  426. }
  427. }
  428. else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
  429. {
  430. if (!current.Configurable.HasValue || current.Configurable.Value == false)
  431. {
  432. if (!current.Writable.HasValue || !current.Writable.Value && desc.Writable.HasValue && desc.Writable.Value)
  433. {
  434. if (throwOnError)
  435. {
  436. throw new JavaScriptException(Engine.TypeError);
  437. }
  438. return false;
  439. }
  440. if (!current.Writable.Value)
  441. {
  442. if (desc.Value.HasValue && !ExpressionInterpreter.SameValue(desc.Value.Value, current.Value.Value))
  443. {
  444. if (throwOnError)
  445. {
  446. throw new JavaScriptException(Engine.TypeError);
  447. }
  448. return false;
  449. }
  450. }
  451. }
  452. }
  453. else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor())
  454. {
  455. if (!current.Configurable.HasValue || !current.Configurable.Value)
  456. {
  457. if ((desc.Set.HasValue && !ExpressionInterpreter.SameValue(desc.Set.Value, current.Set.HasValue ? current.Set.Value : Undefined.Instance))
  458. ||
  459. (desc.Get.HasValue && !ExpressionInterpreter.SameValue(desc.Get.Value, current.Get.HasValue ? current.Get.Value : Undefined.Instance)))
  460. {
  461. if (throwOnError)
  462. {
  463. throw new JavaScriptException(Engine.TypeError);
  464. }
  465. return false;
  466. }
  467. }
  468. }
  469. }
  470. if (desc.Value.HasValue)
  471. {
  472. current.Value = desc.Value;
  473. }
  474. if (desc.Writable.HasValue)
  475. {
  476. current.Writable = desc.Writable;
  477. }
  478. if (desc.Enumerable.HasValue)
  479. {
  480. current.Enumerable = desc.Enumerable;
  481. }
  482. if (desc.Configurable.HasValue)
  483. {
  484. current.Configurable = desc.Configurable;
  485. }
  486. if (desc.Get.HasValue)
  487. {
  488. current.Get = desc.Get;
  489. }
  490. if (desc.Set.HasValue)
  491. {
  492. current.Set = desc.Set;
  493. }
  494. return true;
  495. }
  496. /// <summary>
  497. /// Optimized version of [[Put]] when the property is known to be undeclared already
  498. /// </summary>
  499. /// <param name="name"></param>
  500. /// <param name="value"></param>
  501. /// <param name="writable"></param>
  502. /// <param name="configurable"></param>
  503. /// <param name="enumerable"></param>
  504. public void FastAddProperty(string name, JsValue value, bool writable, bool enumerable, bool configurable)
  505. {
  506. SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
  507. }
  508. /// <summary>
  509. /// Optimized version of [[Put]] when the property is known to be already declared
  510. /// </summary>
  511. /// <param name="name"></param>
  512. /// <param name="value"></param>
  513. public void FastSetProperty(string name, PropertyDescriptor value)
  514. {
  515. SetOwnProperty(name, value);
  516. }
  517. public override string ToString()
  518. {
  519. return TypeConverter.ToString(this);
  520. }
  521. }
  522. }