ObjectInstance.cs 16 KB

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