ObjectInstance.cs 22 KB

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