ObjectInstance.cs 19 KB

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