ObjectInstance.cs 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.Contracts;
  4. using System.Dynamic;
  5. using Jint.Native.Array;
  6. using Jint.Native.Boolean;
  7. using Jint.Native.Date;
  8. using Jint.Native.Function;
  9. using Jint.Native.Number;
  10. using Jint.Native.RegExp;
  11. using Jint.Native.String;
  12. using Jint.Runtime;
  13. using Jint.Runtime.Descriptors;
  14. using Jint.Runtime.Descriptors.Specialized;
  15. using Jint.Runtime.Interop;
  16. namespace Jint.Native.Object
  17. {
  18. public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
  19. {
  20. private const string PropertyNamePrototype = "prototype";
  21. private const string PropertyNameConstructor = "constructor";
  22. private const string PropertyNameLength = "length";
  23. private Dictionary<string, IPropertyDescriptor> _intrinsicProperties;
  24. private MruPropertyCache2<string, IPropertyDescriptor> _properties;
  25. private IPropertyDescriptor _prototype;
  26. private IPropertyDescriptor _constructor;
  27. private IPropertyDescriptor _length;
  28. public ObjectInstance(Engine engine)
  29. {
  30. Engine = engine;
  31. }
  32. public Engine Engine { get; set; }
  33. protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
  34. {
  35. IPropertyDescriptor descriptor;
  36. if (_intrinsicProperties != null && _intrinsicProperties.TryGetValue(symbol.AsSymbol(), out descriptor))
  37. {
  38. value = descriptor.Value;
  39. return true;
  40. }
  41. if (Prototype == null)
  42. {
  43. value = Undefined;
  44. return false;
  45. }
  46. return Prototype.TryGetIntrinsicValue(symbol, out value);
  47. }
  48. public void SetIntrinsicValue(string name, JsValue value, bool writable, bool enumerable, bool configurable)
  49. {
  50. SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
  51. }
  52. protected void SetIntrinsicValue(JsSymbol symbol, JsValue value, bool writable, bool enumerable, bool configurable)
  53. {
  54. if (_intrinsicProperties == null)
  55. {
  56. _intrinsicProperties = new Dictionary<string, IPropertyDescriptor>();
  57. }
  58. _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
  59. }
  60. /// <summary>
  61. /// The prototype of this object.
  62. /// </summary>
  63. public ObjectInstance Prototype { get; set; }
  64. /// <summary>
  65. /// If true, own properties may be added to the
  66. /// object.
  67. /// </summary>
  68. public bool Extensible { get; set; }
  69. /// <summary>
  70. /// A String value indicating a specification defined
  71. /// classification of objects.
  72. /// </summary>
  73. public virtual string Class => "Object";
  74. public virtual IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
  75. {
  76. EnsureInitialized();
  77. if (_prototype != null)
  78. {
  79. yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNamePrototype, _prototype);
  80. }
  81. if (_constructor != null)
  82. {
  83. yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameConstructor, _constructor);
  84. }
  85. if (_length != null)
  86. {
  87. yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
  88. }
  89. if (_properties != null)
  90. {
  91. foreach (var pair in _properties.GetEnumerator())
  92. {
  93. yield return pair;
  94. }
  95. }
  96. }
  97. protected void AddProperty(string propertyName, IPropertyDescriptor descriptor)
  98. {
  99. if (propertyName == PropertyNamePrototype)
  100. {
  101. _prototype = descriptor;
  102. return;
  103. }
  104. if (propertyName == PropertyNameConstructor)
  105. {
  106. _constructor = descriptor;
  107. return;
  108. }
  109. if (propertyName == PropertyNameLength)
  110. {
  111. _length = descriptor;
  112. return;
  113. }
  114. if (_properties == null)
  115. {
  116. _properties = new MruPropertyCache2<string, IPropertyDescriptor>();
  117. }
  118. _properties.Add(propertyName, descriptor);
  119. }
  120. protected bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
  121. {
  122. if (propertyName == PropertyNamePrototype)
  123. {
  124. descriptor = _prototype;
  125. return _prototype != null;
  126. }
  127. if (propertyName == PropertyNameConstructor)
  128. {
  129. descriptor = _constructor;
  130. return _constructor != null;
  131. }
  132. if (propertyName == PropertyNameLength)
  133. {
  134. descriptor = _length;
  135. return _length != null;
  136. }
  137. if (_properties == null)
  138. {
  139. descriptor = null;
  140. return false;
  141. }
  142. return _properties.TryGetValue(propertyName, out descriptor);
  143. }
  144. public virtual bool HasOwnProperty(string propertyName)
  145. {
  146. EnsureInitialized();
  147. if (propertyName == PropertyNamePrototype)
  148. {
  149. return _prototype != null;
  150. }
  151. if (propertyName == PropertyNameConstructor)
  152. {
  153. return _constructor != null;
  154. }
  155. if (propertyName == PropertyNameLength)
  156. {
  157. return _length != null;
  158. }
  159. return _properties?.ContainsKey(propertyName) ?? false;
  160. }
  161. public virtual void RemoveOwnProperty(string propertyName)
  162. {
  163. EnsureInitialized();
  164. if (propertyName == PropertyNamePrototype)
  165. {
  166. _prototype = null;
  167. }
  168. if (propertyName == PropertyNameConstructor)
  169. {
  170. _constructor = null;
  171. }
  172. if (propertyName == PropertyNameLength)
  173. {
  174. _length = null;
  175. }
  176. _properties?.Remove(propertyName);
  177. }
  178. /// <summary>
  179. /// Returns the value of the named property.
  180. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.3
  181. /// </summary>
  182. /// <param name="propertyName"></param>
  183. /// <returns></returns>
  184. public virtual JsValue Get(string propertyName)
  185. {
  186. var desc = GetProperty(propertyName);
  187. if (desc == PropertyDescriptor.Undefined)
  188. {
  189. return Undefined;
  190. }
  191. if (desc.IsDataDescriptor())
  192. {
  193. var val = desc.Value;
  194. return val != null ? val : Undefined;
  195. }
  196. var getter = desc.Get != null ? desc.Get : Undefined;
  197. if (getter.IsUndefined())
  198. {
  199. return JsValue.Undefined;
  200. }
  201. // if getter is not undefined it must be ICallable
  202. var callable = getter.TryCast<ICallable>();
  203. return callable.Call(this, Arguments.Empty);
  204. }
  205. /// <summary>
  206. /// Returns the Property Descriptor of the named
  207. /// own property of this object, or undefined if
  208. /// absent.
  209. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.1
  210. /// </summary>
  211. /// <param name="propertyName"></param>
  212. /// <returns></returns>
  213. public virtual IPropertyDescriptor GetOwnProperty(string propertyName)
  214. {
  215. EnsureInitialized();
  216. if (propertyName == PropertyNamePrototype)
  217. {
  218. return _prototype ?? PropertyDescriptor.Undefined;
  219. }
  220. if (propertyName == PropertyNameConstructor)
  221. {
  222. return _constructor ?? PropertyDescriptor.Undefined;
  223. }
  224. if (propertyName == PropertyNameLength)
  225. {
  226. return _length ?? PropertyDescriptor.Undefined;
  227. }
  228. if (_properties != null && _properties.TryGetValue(propertyName, out var x))
  229. {
  230. return x;
  231. }
  232. return PropertyDescriptor.Undefined;
  233. }
  234. protected internal virtual void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
  235. {
  236. EnsureInitialized();
  237. if (propertyName == PropertyNamePrototype)
  238. {
  239. _prototype = desc;
  240. return;
  241. }
  242. if (propertyName == PropertyNameConstructor)
  243. {
  244. _constructor = desc;
  245. return;
  246. }
  247. if (propertyName == PropertyNameLength)
  248. {
  249. _length = desc;
  250. return;
  251. }
  252. if (_properties == null)
  253. {
  254. _properties = new MruPropertyCache2<string, IPropertyDescriptor>();
  255. }
  256. _properties[propertyName] = desc;
  257. }
  258. /// <summary>
  259. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.2
  260. /// </summary>
  261. /// <param name="propertyName"></param>
  262. /// <returns></returns>
  263. public IPropertyDescriptor GetProperty(string propertyName)
  264. {
  265. var prop = GetOwnProperty(propertyName);
  266. if (prop != PropertyDescriptor.Undefined)
  267. {
  268. return prop;
  269. }
  270. if (Prototype == null)
  271. {
  272. return PropertyDescriptor.Undefined;
  273. }
  274. return Prototype.GetProperty(propertyName);
  275. }
  276. public bool TryGetValue(string propertyName, out JsValue value)
  277. {
  278. value = Undefined;
  279. var desc = GetOwnProperty(propertyName);
  280. if (desc != null && desc != PropertyDescriptor.Undefined)
  281. {
  282. if (desc == PropertyDescriptor.Undefined)
  283. {
  284. return false;
  285. }
  286. if (desc.IsDataDescriptor() && desc.Value != null)
  287. {
  288. value = desc.Value;
  289. return true;
  290. }
  291. var getter = desc.Get != null ? desc.Get : Undefined;
  292. if (getter.IsUndefined())
  293. {
  294. value = Undefined;
  295. return false;
  296. }
  297. // if getter is not undefined it must be ICallable
  298. var callable = getter.TryCast<ICallable>();
  299. value = callable.Call(this, Arguments.Empty);
  300. return true;
  301. }
  302. if (Prototype == null)
  303. {
  304. return false;
  305. }
  306. return Prototype.TryGetValue(propertyName, out value);
  307. }
  308. /// <summary>
  309. /// Sets the specified named property to the value
  310. /// of the second parameter. The flag controls
  311. /// failure handling.
  312. /// </summary>
  313. /// <param name="propertyName"></param>
  314. /// <param name="value"></param>
  315. /// <param name="throwOnError"></param>
  316. public virtual void Put(string propertyName, JsValue value, bool throwOnError)
  317. {
  318. if (!CanPut(propertyName))
  319. {
  320. if (throwOnError)
  321. {
  322. throw new JavaScriptException(Engine.TypeError);
  323. }
  324. return;
  325. }
  326. var ownDesc = GetOwnProperty(propertyName);
  327. if (ownDesc.IsDataDescriptor())
  328. {
  329. ownDesc.Value = value;
  330. return;
  331. // as per specification
  332. // var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
  333. // DefineOwnProperty(propertyName, valueDesc, throwOnError);
  334. // return;
  335. }
  336. // property is an accessor or inherited
  337. var desc = GetProperty(propertyName);
  338. if (desc.IsAccessorDescriptor())
  339. {
  340. var setter = desc.Set.TryCast<ICallable>();
  341. setter.Call(this, new[] {value});
  342. }
  343. else
  344. {
  345. var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
  346. DefineOwnProperty(propertyName, newDesc, throwOnError);
  347. }
  348. }
  349. /// <summary>
  350. /// Returns a Boolean value indicating whether a
  351. /// [[Put]] operation with PropertyName can be
  352. /// performed.
  353. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.4
  354. /// </summary>
  355. /// <param name="propertyName"></param>
  356. /// <returns></returns>
  357. public bool CanPut(string propertyName)
  358. {
  359. var desc = GetOwnProperty(propertyName);
  360. if (desc != PropertyDescriptor.Undefined)
  361. {
  362. if (desc.IsAccessorDescriptor())
  363. {
  364. if (desc.Set == null || desc.Set.IsUndefined())
  365. {
  366. return false;
  367. }
  368. return true;
  369. }
  370. return desc.Writable.HasValue && desc.Writable.Value;
  371. }
  372. if (Prototype == null)
  373. {
  374. return Extensible;
  375. }
  376. var inherited = Prototype.GetProperty(propertyName);
  377. if (inherited == PropertyDescriptor.Undefined)
  378. {
  379. return Extensible;
  380. }
  381. if (inherited.IsAccessorDescriptor())
  382. {
  383. if (inherited.Set == null || inherited.Set.IsUndefined())
  384. {
  385. return false;
  386. }
  387. return true;
  388. }
  389. if (!Extensible)
  390. {
  391. return false;
  392. }
  393. else
  394. {
  395. return inherited.Writable.HasValue && inherited.Writable.Value;
  396. }
  397. }
  398. /// <summary>
  399. /// Returns a Boolean value indicating whether the
  400. /// object already has a property with the given
  401. /// name.
  402. /// </summary>
  403. /// <param name="propertyName"></param>
  404. /// <returns></returns>
  405. public bool HasProperty(string propertyName)
  406. {
  407. return GetProperty(propertyName) != PropertyDescriptor.Undefined;
  408. }
  409. /// <summary>
  410. /// Removes the specified named own property
  411. /// from the object. The flag controls failure
  412. /// handling.
  413. /// </summary>
  414. /// <param name="propertyName"></param>
  415. /// <param name="throwOnError"></param>
  416. /// <returns></returns>
  417. public virtual bool Delete(string propertyName, bool throwOnError)
  418. {
  419. var desc = GetOwnProperty(propertyName);
  420. if (desc == PropertyDescriptor.Undefined)
  421. {
  422. return true;
  423. }
  424. if (desc.Configurable.HasValue && desc.Configurable.Value)
  425. {
  426. RemoveOwnProperty(propertyName);
  427. return true;
  428. }
  429. else
  430. {
  431. if (throwOnError)
  432. {
  433. throw new JavaScriptException(Engine.TypeError);
  434. }
  435. return false;
  436. }
  437. }
  438. /// <summary>
  439. /// Hint is a String. Returns a default value for the
  440. /// object.
  441. /// </summary>
  442. /// <param name="hint"></param>
  443. /// <returns></returns>
  444. public JsValue DefaultValue(Types hint)
  445. {
  446. EnsureInitialized();
  447. if (hint == Types.String || (hint == Types.None && Class == "Date"))
  448. {
  449. var toString = Get("toString").TryCast<ICallable>();
  450. if (toString != null)
  451. {
  452. var str = toString.Call(this, Arguments.Empty);
  453. if (str.IsPrimitive())
  454. {
  455. return str;
  456. }
  457. }
  458. var valueOf = Get("valueOf").TryCast<ICallable>();
  459. if (valueOf != null)
  460. {
  461. var val = valueOf.Call(this, Arguments.Empty);
  462. if (val.IsPrimitive())
  463. {
  464. return val;
  465. }
  466. }
  467. throw new JavaScriptException(Engine.TypeError);
  468. }
  469. if (hint == Types.Number || hint == Types.None)
  470. {
  471. var valueOf = Get("valueOf").TryCast<ICallable>();
  472. if (valueOf != null)
  473. {
  474. var val = valueOf.Call(this, Arguments.Empty);
  475. if (val.IsPrimitive())
  476. {
  477. return val;
  478. }
  479. }
  480. var toString = Get("toString").TryCast<ICallable>();
  481. if (toString != null)
  482. {
  483. var str = toString.Call(this, Arguments.Empty);
  484. if (str.IsPrimitive())
  485. {
  486. return str;
  487. }
  488. }
  489. throw new JavaScriptException(Engine.TypeError);
  490. }
  491. return ToString();
  492. }
  493. /// <summary>
  494. /// Creates or alters the named own property to
  495. /// have the state described by a Property
  496. /// Descriptor. The flag controls failure handling.
  497. /// </summary>
  498. /// <param name="propertyName"></param>
  499. /// <param name="desc"></param>
  500. /// <param name="throwOnError"></param>
  501. /// <returns></returns>
  502. public virtual bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
  503. {
  504. var current = GetOwnProperty(propertyName);
  505. if (current == desc)
  506. {
  507. return true;
  508. }
  509. if (current == PropertyDescriptor.Undefined)
  510. {
  511. if (!Extensible)
  512. {
  513. if (throwOnError)
  514. {
  515. throw new JavaScriptException(Engine.TypeError);
  516. }
  517. return false;
  518. }
  519. else
  520. {
  521. if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
  522. {
  523. IPropertyDescriptor propertyDescriptor;
  524. if (desc.Configurable.GetValueOrDefault() && desc.Enumerable.GetValueOrDefault() && desc.Writable.GetValueOrDefault())
  525. {
  526. propertyDescriptor = new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : Undefined);
  527. }
  528. else if (!desc.Configurable.GetValueOrDefault() && !desc.Enumerable.GetValueOrDefault() && !desc.Writable.GetValueOrDefault())
  529. {
  530. propertyDescriptor = new AllForbiddenPropertyDescriptor(desc.Value != null ? desc.Value : Undefined);
  531. }
  532. else
  533. {
  534. propertyDescriptor = new PropertyDescriptor(desc)
  535. {
  536. Value = desc.Value != null ? desc.Value : Undefined,
  537. Writable = desc.Writable.HasValue ? desc.Writable.Value : false,
  538. Enumerable = desc.Enumerable.HasValue ? desc.Enumerable.Value : false,
  539. Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false
  540. };
  541. }
  542. SetOwnProperty(propertyName, propertyDescriptor);
  543. }
  544. else
  545. {
  546. SetOwnProperty(propertyName, new PropertyDescriptor(desc)
  547. {
  548. Get = desc.Get,
  549. Set = desc.Set,
  550. Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false,
  551. Configurable = desc.Configurable.HasValue ? desc.Configurable : false,
  552. });
  553. }
  554. }
  555. return true;
  556. }
  557. // Step 5
  558. if (!current.Configurable.HasValue &&
  559. !current.Enumerable.HasValue &&
  560. !current.Writable.HasValue &&
  561. current.Get == null &&
  562. current.Set == null &&
  563. current.Value == null)
  564. {
  565. return true;
  566. }
  567. // Step 6
  568. if (
  569. current.Configurable == desc.Configurable &&
  570. current.Writable == desc.Writable &&
  571. current.Enumerable == desc.Enumerable &&
  572. ((current.Get == null && desc.Get == null) || (current.Get != null && desc.Get != null && ExpressionInterpreter.SameValue(current.Get, desc.Get))) &&
  573. ((current.Set == null && desc.Set == null) || (current.Set != null && desc.Set != null && ExpressionInterpreter.SameValue(current.Set, desc.Set))) &&
  574. ((current.Value == null && desc.Value == null) || (current.Value != null && desc.Value != null && ExpressionInterpreter.StrictlyEqual(current.Value, desc.Value)))
  575. )
  576. {
  577. return true;
  578. }
  579. if (!current.Configurable.HasValue || !current.Configurable.Value)
  580. {
  581. if (desc.Configurable.HasValue && desc.Configurable.Value)
  582. {
  583. if (throwOnError)
  584. {
  585. throw new JavaScriptException(Engine.TypeError);
  586. }
  587. return false;
  588. }
  589. if (desc.Enumerable.HasValue && (!current.Enumerable.HasValue || desc.Enumerable.Value != current.Enumerable.Value))
  590. {
  591. if (throwOnError)
  592. {
  593. throw new JavaScriptException(Engine.TypeError);
  594. }
  595. return false;
  596. }
  597. }
  598. if (!desc.IsGenericDescriptor())
  599. {
  600. if (current.IsDataDescriptor() != desc.IsDataDescriptor())
  601. {
  602. if (!current.Configurable.HasValue || !current.Configurable.Value)
  603. {
  604. if (throwOnError)
  605. {
  606. throw new JavaScriptException(Engine.TypeError);
  607. }
  608. return false;
  609. }
  610. if (current.IsDataDescriptor())
  611. {
  612. SetOwnProperty(propertyName, current = new PropertyDescriptor(
  613. get: JsValue.Undefined,
  614. set: JsValue.Undefined,
  615. enumerable: current.Enumerable,
  616. configurable: current.Configurable
  617. ));
  618. }
  619. else
  620. {
  621. SetOwnProperty(propertyName, current = new PropertyDescriptor(
  622. value: JsValue.Undefined,
  623. writable: null,
  624. enumerable: current.Enumerable,
  625. configurable: current.Configurable
  626. ));
  627. }
  628. }
  629. else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
  630. {
  631. if (!current.Configurable.HasValue || current.Configurable.Value == false)
  632. {
  633. if (!current.Writable.HasValue || !current.Writable.Value && desc.Writable.HasValue && desc.Writable.Value)
  634. {
  635. if (throwOnError)
  636. {
  637. throw new JavaScriptException(Engine.TypeError);
  638. }
  639. return false;
  640. }
  641. if (!current.Writable.Value)
  642. {
  643. if (desc.Value != null && !ExpressionInterpreter.SameValue(desc.Value, current.Value))
  644. {
  645. if (throwOnError)
  646. {
  647. throw new JavaScriptException(Engine.TypeError);
  648. }
  649. return false;
  650. }
  651. }
  652. }
  653. }
  654. else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor())
  655. {
  656. if (!current.Configurable.HasValue || !current.Configurable.Value)
  657. {
  658. if ((desc.Set != null && !ExpressionInterpreter.SameValue(desc.Set, current.Set != null ? current.Set : Undefined))
  659. ||
  660. (desc.Get != null && !ExpressionInterpreter.SameValue(desc.Get, current.Get != null ? current.Get : Undefined)))
  661. {
  662. if (throwOnError)
  663. {
  664. throw new JavaScriptException(Engine.TypeError);
  665. }
  666. return false;
  667. }
  668. }
  669. }
  670. }
  671. if (desc.Value != null)
  672. {
  673. current.Value = desc.Value;
  674. }
  675. PropertyDescriptor mutable = null;
  676. if (desc.Writable.HasValue)
  677. {
  678. current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
  679. mutable.Writable = desc.Writable;
  680. }
  681. if (desc.Enumerable.HasValue)
  682. {
  683. current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
  684. mutable.Enumerable = desc.Enumerable;
  685. }
  686. if (desc.Configurable.HasValue)
  687. {
  688. current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
  689. mutable.Configurable = desc.Configurable;
  690. }
  691. if (desc.Get != null)
  692. {
  693. current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
  694. mutable.Get = desc.Get;
  695. }
  696. if (desc.Set != null)
  697. {
  698. mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
  699. mutable.Set = desc.Set;
  700. }
  701. if (mutable != null)
  702. {
  703. FastSetProperty(propertyName, mutable);
  704. }
  705. return true;
  706. }
  707. /// <summary>
  708. /// Optimized version of [[Put]] when the property is known to be undeclared already
  709. /// </summary>
  710. /// <param name="name"></param>
  711. /// <param name="value"></param>
  712. /// <param name="writable"></param>
  713. /// <param name="configurable"></param>
  714. /// <param name="enumerable"></param>
  715. public void FastAddProperty(string name, JsValue value, bool writable, bool enumerable, bool configurable)
  716. {
  717. SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
  718. }
  719. /// <summary>
  720. /// Optimized version of [[Put]] when the property is known to be already declared
  721. /// </summary>
  722. /// <param name="name"></param>
  723. /// <param name="value"></param>
  724. public void FastSetProperty(string name, IPropertyDescriptor value)
  725. {
  726. SetOwnProperty(name, value);
  727. }
  728. protected virtual void EnsureInitialized()
  729. {
  730. }
  731. public override string ToString()
  732. {
  733. return TypeConverter.ToString(this);
  734. }
  735. protected uint GetLengthValue() => TypeConverter.ToUint32(_length.Value);
  736. public override Types Type => Types.Object;
  737. [Pure]
  738. public override bool IsArray()
  739. {
  740. return this is ArrayInstance;
  741. }
  742. [Pure]
  743. public override bool IsDate()
  744. {
  745. return this is DateInstance;
  746. }
  747. [Pure]
  748. public override bool IsRegExp()
  749. {
  750. return this is RegExpInstance;
  751. }
  752. [Pure]
  753. public override ObjectInstance AsObject()
  754. {
  755. return this;
  756. }
  757. [Pure]
  758. public override TInstance AsInstance<TInstance>()
  759. {
  760. return this as TInstance;
  761. }
  762. [Pure]
  763. public override ArrayInstance AsArray()
  764. {
  765. if (!IsArray())
  766. {
  767. throw new ArgumentException("The value is not an array");
  768. }
  769. return this as ArrayInstance;
  770. }
  771. [Pure]
  772. public override DateInstance AsDate()
  773. {
  774. if (!IsDate())
  775. {
  776. throw new ArgumentException("The value is not a date");
  777. }
  778. return this as DateInstance;
  779. }
  780. [Pure]
  781. public override RegExpInstance AsRegExp()
  782. {
  783. if (!IsRegExp())
  784. {
  785. throw new ArgumentException("The value is not a regex");
  786. }
  787. return this as RegExpInstance;
  788. }
  789. public override bool Is<T>()
  790. {
  791. return this is T;
  792. }
  793. public override T As<T>()
  794. {
  795. return this as T;
  796. }
  797. public override object ToObject()
  798. {
  799. if (this is IObjectWrapper wrapper)
  800. {
  801. return wrapper.Target;
  802. }
  803. switch (Class)
  804. {
  805. case "Array":
  806. if (this is ArrayInstance arrayInstance)
  807. {
  808. var len = TypeConverter.ToInt32(arrayInstance.Get("length"));
  809. var result = new object[len];
  810. for (var k = 0; k < len; k++)
  811. {
  812. var pk = TypeConverter.ToString(k);
  813. var kpresent = arrayInstance.HasProperty(pk);
  814. if (kpresent)
  815. {
  816. var kvalue = arrayInstance.Get(pk);
  817. result[k] = kvalue.ToObject();
  818. }
  819. else
  820. {
  821. result[k] = null;
  822. }
  823. }
  824. return result;
  825. }
  826. break;
  827. case "String":
  828. if (this is StringInstance stringInstance)
  829. {
  830. return stringInstance.PrimitiveValue.AsString();
  831. }
  832. break;
  833. case "Date":
  834. if (this is DateInstance dateInstance)
  835. {
  836. return dateInstance.ToDateTime();
  837. }
  838. break;
  839. case "Boolean":
  840. if (this is BooleanInstance booleanInstance)
  841. {
  842. return booleanInstance.PrimitiveValue.AsBoolean();
  843. }
  844. break;
  845. case "Function":
  846. if (this is FunctionInstance function)
  847. {
  848. return (Func<JsValue, JsValue[], JsValue>) function.Call;
  849. }
  850. break;
  851. case "Number":
  852. if (this is NumberInstance numberInstance)
  853. {
  854. return numberInstance.NumberData.AsNumber();
  855. }
  856. break;
  857. case "RegExp":
  858. if (this is RegExpInstance regeExpInstance)
  859. {
  860. return regeExpInstance.Value;
  861. }
  862. break;
  863. case "Arguments":
  864. case "Object":
  865. #if __IOS__
  866. IDictionary<string, object> o = new Dictionary<string, object>();
  867. #else
  868. IDictionary<string, object> o = new ExpandoObject();
  869. #endif
  870. foreach (var p in GetOwnProperties())
  871. {
  872. if (!p.Value.Enumerable.HasValue || p.Value.Enumerable.Value == false)
  873. {
  874. continue;
  875. }
  876. o.Add(p.Key, Get(p.Key).ToObject());
  877. }
  878. return o;
  879. }
  880. return this;
  881. }
  882. public override bool Equals(JsValue obj)
  883. {
  884. if (ReferenceEquals(null, obj))
  885. {
  886. return false;
  887. }
  888. if (!(obj is ObjectInstance s))
  889. {
  890. return false;
  891. }
  892. return Equals(s);
  893. }
  894. public bool Equals(ObjectInstance other)
  895. {
  896. if (ReferenceEquals(null, other))
  897. {
  898. return false;
  899. }
  900. if (ReferenceEquals(this, other))
  901. {
  902. return true;
  903. }
  904. return false;
  905. }
  906. }
  907. }