ObjectInstance.cs 14 KB

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