ObjectInstance.cs 21 KB

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