ObjectInstance.cs 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653
  1. using System.Diagnostics;
  2. using System.Diagnostics.CodeAnalysis;
  3. using System.Runtime.CompilerServices;
  4. using Jint.Collections;
  5. using Jint.Native.Array;
  6. using Jint.Native.BigInt;
  7. using Jint.Native.Boolean;
  8. using Jint.Native.Function;
  9. using Jint.Native.Number;
  10. using Jint.Native.RegExp;
  11. using Jint.Native.String;
  12. using Jint.Native.Symbol;
  13. using Jint.Native.TypedArray;
  14. using Jint.Runtime;
  15. using Jint.Runtime.Descriptors;
  16. using Jint.Runtime.Interop;
  17. namespace Jint.Native.Object
  18. {
  19. public partial class ObjectInstance : JsValue, IEquatable<ObjectInstance>
  20. {
  21. private bool _initialized;
  22. private readonly ObjectClass _class;
  23. internal PropertyDictionary? _properties;
  24. internal SymbolDictionary? _symbols;
  25. internal ObjectInstance? _prototype;
  26. protected readonly Engine _engine;
  27. protected ObjectInstance(Engine engine) : this(engine, ObjectClass.Object)
  28. {
  29. }
  30. internal ObjectInstance(
  31. Engine engine,
  32. ObjectClass objectClass = ObjectClass.Object,
  33. InternalTypes type = InternalTypes.Object)
  34. : base(type)
  35. {
  36. _engine = engine;
  37. _class = objectClass;
  38. // if engine is ready, we can take default prototype for object
  39. _prototype = engine.Realm.Intrinsics?.Object?.PrototypeObject;
  40. Extensible = true;
  41. }
  42. public Engine Engine
  43. {
  44. [DebuggerStepThrough]
  45. get => _engine;
  46. }
  47. /// <summary>
  48. /// The prototype of this object.
  49. /// </summary>
  50. public ObjectInstance? Prototype
  51. {
  52. [DebuggerStepThrough]
  53. get => GetPrototypeOf();
  54. }
  55. /// <summary>
  56. /// If true, own properties may be added to the
  57. /// object.
  58. /// </summary>
  59. public virtual bool Extensible { get; private set; }
  60. internal PropertyDictionary? Properties
  61. {
  62. [DebuggerStepThrough]
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. get => _properties;
  65. }
  66. /// <summary>
  67. /// A value indicating a specification defined classification of objects.
  68. /// </summary>
  69. internal ObjectClass Class
  70. {
  71. [DebuggerStepThrough]
  72. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  73. get => _class;
  74. }
  75. public JsValue this[JsValue property]
  76. {
  77. get => Get(property);
  78. set => Set(property, value);
  79. }
  80. /// <summary>
  81. /// https://tc39.es/ecma262/#sec-construct
  82. /// </summary>
  83. internal static ObjectInstance Construct(IConstructor f, JsValue[]? argumentsList = null, IConstructor? newTarget = null)
  84. {
  85. newTarget ??= f;
  86. argumentsList ??= System.Array.Empty<JsValue>();
  87. return f.Construct(argumentsList, (JsValue) newTarget);
  88. }
  89. /// <summary>
  90. /// https://tc39.es/ecma262/#sec-speciesconstructor
  91. /// </summary>
  92. internal static IConstructor SpeciesConstructor(ObjectInstance o, IConstructor defaultConstructor)
  93. {
  94. var c = o.Get(CommonProperties.Constructor);
  95. if (c.IsUndefined())
  96. {
  97. return defaultConstructor;
  98. }
  99. var oi = c as ObjectInstance;
  100. if (oi is null)
  101. {
  102. ExceptionHelper.ThrowTypeError(o._engine.Realm);
  103. }
  104. var s = oi.Get(GlobalSymbolRegistry.Species);
  105. if (s.IsNullOrUndefined())
  106. {
  107. return defaultConstructor;
  108. }
  109. if (s.IsConstructor)
  110. {
  111. return (IConstructor) s;
  112. }
  113. ExceptionHelper.ThrowTypeError(o._engine.Realm);
  114. return null;
  115. }
  116. internal void SetProperties(StringDictionarySlim<PropertyDescriptor> properties) => SetProperties(new PropertyDictionary(properties));
  117. internal void SetProperties(PropertyDictionary? properties)
  118. {
  119. if (properties != null)
  120. {
  121. properties.CheckExistingKeys = true;
  122. }
  123. _properties = properties;
  124. }
  125. internal void SetSymbols(SymbolDictionary? symbols)
  126. {
  127. _symbols = symbols;
  128. }
  129. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  130. internal void SetProperty(JsValue property, PropertyDescriptor value)
  131. {
  132. if (property is JsString jsString)
  133. {
  134. SetProperty(jsString.ToString(), value);
  135. }
  136. else
  137. {
  138. SetPropertyUnlikely(property, value);
  139. }
  140. }
  141. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  142. internal void SetProperty(string property, PropertyDescriptor value)
  143. {
  144. Key key = property;
  145. SetProperty(key, value);
  146. }
  147. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  148. internal void SetProperty(Key property, PropertyDescriptor value)
  149. {
  150. _properties ??= new PropertyDictionary();
  151. _properties[property] = value;
  152. }
  153. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  154. internal void SetDataProperty(string property, JsValue value)
  155. {
  156. _properties ??= new PropertyDictionary();
  157. _properties[property] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  158. }
  159. [MethodImpl(MethodImplOptions.NoInlining)]
  160. private void SetPropertyUnlikely(JsValue property, PropertyDescriptor value)
  161. {
  162. var propertyKey = TypeConverter.ToPropertyKey(property);
  163. if (!property.IsSymbol())
  164. {
  165. _properties ??= new PropertyDictionary();
  166. _properties[TypeConverter.ToString(propertyKey)] = value;
  167. }
  168. else
  169. {
  170. _symbols ??= new SymbolDictionary();
  171. _symbols[(JsSymbol) propertyKey] = value;
  172. }
  173. }
  174. internal void ClearProperties()
  175. {
  176. _properties?.Clear();
  177. _symbols?.Clear();
  178. }
  179. public virtual IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
  180. {
  181. EnsureInitialized();
  182. if (_properties != null)
  183. {
  184. foreach (var pair in _properties)
  185. {
  186. yield return new KeyValuePair<JsValue, PropertyDescriptor>(new JsString(pair.Key), pair.Value);
  187. }
  188. }
  189. if (_symbols != null)
  190. {
  191. foreach (var pair in _symbols)
  192. {
  193. yield return new KeyValuePair<JsValue, PropertyDescriptor>(pair.Key, pair.Value);
  194. }
  195. }
  196. }
  197. public virtual List<JsValue> GetOwnPropertyKeys(Types types = Types.String | Types.Symbol)
  198. {
  199. EnsureInitialized();
  200. var returningSymbols = (types & Types.Symbol) != 0 && _symbols?.Count > 0;
  201. var returningStringKeys = (types & Types.String) != 0 && _properties?.Count > 0;
  202. var propertyKeys = new List<JsValue>();
  203. if ((types & Types.String) != 0)
  204. {
  205. var initialOwnStringPropertyKeys = GetInitialOwnStringPropertyKeys();
  206. if (!ReferenceEquals(initialOwnStringPropertyKeys, System.Linq.Enumerable.Empty<JsValue>()))
  207. {
  208. propertyKeys.AddRange(initialOwnStringPropertyKeys);
  209. }
  210. }
  211. // check fast case where we don't need to sort, which should be the common case
  212. if (!returningSymbols)
  213. {
  214. if (!returningStringKeys)
  215. {
  216. return propertyKeys;
  217. }
  218. var propertyKeyCount = propertyKeys.Count;
  219. propertyKeys.Capacity += _properties!.Count;
  220. foreach (var pair in _properties)
  221. {
  222. // check if we can rely on the property name not being an unsigned number
  223. var c = pair.Key.Name.Length > 0 ? pair.Key.Name[0] : 'a';
  224. if (char.IsDigit(c) && propertyKeyCount + _properties.Count > 1)
  225. {
  226. // jump to slow path, return list to original state
  227. propertyKeys.RemoveRange(propertyKeyCount, propertyKeys.Count - propertyKeyCount);
  228. return GetOwnPropertyKeysSorted(propertyKeys, returningStringKeys, returningSymbols);
  229. }
  230. propertyKeys.Add(new JsString(pair.Key.Name));
  231. }
  232. // seems good
  233. return propertyKeys;
  234. }
  235. if ((types & Types.String) == 0 && (types & Types.Symbol) != 0)
  236. {
  237. // only symbols requested
  238. if (_symbols != null)
  239. {
  240. foreach (var pair in _symbols!)
  241. {
  242. propertyKeys.Add(pair.Key);
  243. }
  244. }
  245. return propertyKeys;
  246. }
  247. return GetOwnPropertyKeysSorted(propertyKeys, returningStringKeys, returningSymbols);
  248. }
  249. private List<JsValue> GetOwnPropertyKeysSorted(List<JsValue> initialOwnPropertyKeys, bool returningStringKeys, bool returningSymbols)
  250. {
  251. var keys = new List<JsValue>(_properties?.Count ?? 0 + _symbols?.Count ?? 0 + initialOwnPropertyKeys.Count);
  252. if (returningStringKeys && _properties != null)
  253. {
  254. foreach (var pair in _properties)
  255. {
  256. var propertyName = pair.Key.Name;
  257. var arrayIndex = ArrayInstance.ParseArrayIndex(propertyName);
  258. if (arrayIndex < ArrayOperations.MaxArrayLength)
  259. {
  260. keys.Add(JsString.Create(arrayIndex));
  261. }
  262. else
  263. {
  264. initialOwnPropertyKeys.Add(new JsString(propertyName));
  265. }
  266. }
  267. }
  268. keys.Sort((v1, v2) => TypeConverter.ToNumber(v1).CompareTo(TypeConverter.ToNumber(v2)));
  269. keys.AddRange(initialOwnPropertyKeys);
  270. if (returningSymbols)
  271. {
  272. foreach (var pair in _symbols!)
  273. {
  274. keys.Add(pair.Key);
  275. }
  276. }
  277. return keys;
  278. }
  279. internal virtual IEnumerable<JsValue> GetInitialOwnStringPropertyKeys() => System.Linq.Enumerable.Empty<JsValue>();
  280. protected virtual void AddProperty(JsValue property, PropertyDescriptor descriptor)
  281. {
  282. SetProperty(property, descriptor);
  283. }
  284. protected virtual bool TryGetProperty(JsValue property, [NotNullWhen(true)] out PropertyDescriptor? descriptor)
  285. {
  286. descriptor = null;
  287. var key = TypeConverter.ToPropertyKey(property);
  288. if (!key.IsSymbol())
  289. {
  290. return _properties?.TryGetValue(TypeConverter.ToString(key), out descriptor) == true;
  291. }
  292. return _symbols?.TryGetValue((JsSymbol) key, out descriptor) == true;
  293. }
  294. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  295. public bool HasOwnProperty(JsValue property)
  296. {
  297. return !ReferenceEquals(GetOwnProperty(property), PropertyDescriptor.Undefined);
  298. }
  299. public virtual void RemoveOwnProperty(JsValue property)
  300. {
  301. EnsureInitialized();
  302. var key = TypeConverter.ToPropertyKey(property);
  303. if (!key.IsSymbol())
  304. {
  305. _properties?.Remove(TypeConverter.ToString(key));
  306. return;
  307. }
  308. _symbols?.Remove((JsSymbol) key);
  309. }
  310. public override JsValue Get(JsValue property, JsValue receiver)
  311. {
  312. if ((_type & InternalTypes.PlainObject) != 0 && ReferenceEquals(this, receiver) && property is JsString jsString)
  313. {
  314. EnsureInitialized();
  315. if (_properties?.TryGetValue(jsString.ToString(), out var ownDesc) == true)
  316. {
  317. return UnwrapJsValue(ownDesc, receiver);
  318. }
  319. }
  320. else
  321. {
  322. var desc = GetOwnProperty(property);
  323. if (desc != PropertyDescriptor.Undefined)
  324. {
  325. return UnwrapJsValue(desc, receiver);
  326. }
  327. }
  328. return Prototype?.Get(property, receiver) ?? Undefined;
  329. }
  330. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  331. internal JsValue UnwrapJsValue(PropertyDescriptor desc)
  332. {
  333. return UnwrapJsValue(desc, this);
  334. }
  335. internal static JsValue UnwrapJsValue(PropertyDescriptor desc, JsValue thisObject)
  336. {
  337. var value = (desc._flags & PropertyFlag.CustomJsValue) != 0
  338. ? desc.CustomValue
  339. : desc._value;
  340. // IsDataDescriptor inlined
  341. if ((desc._flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0 || value is not null)
  342. {
  343. return value ?? Undefined;
  344. }
  345. return UnwrapFromGetter(desc, thisObject);
  346. }
  347. /// <summary>
  348. /// A rarer case.
  349. /// </summary>
  350. [MethodImpl(MethodImplOptions.NoInlining)]
  351. private static JsValue UnwrapFromGetter(PropertyDescriptor desc, JsValue thisObject)
  352. {
  353. var getter = desc.Get ?? Undefined;
  354. if (getter.IsUndefined())
  355. {
  356. return Undefined;
  357. }
  358. var functionInstance = (FunctionInstance) getter;
  359. return functionInstance._engine.Call(functionInstance, thisObject);
  360. }
  361. /// <summary>
  362. /// Returns the Property Descriptor of the named
  363. /// own property of this object, or undefined if
  364. /// absent.
  365. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.1
  366. /// </summary>
  367. public virtual PropertyDescriptor GetOwnProperty(JsValue property)
  368. {
  369. EnsureInitialized();
  370. PropertyDescriptor? descriptor = null;
  371. var key = TypeConverter.ToPropertyKey(property);
  372. if (!key.IsSymbol())
  373. {
  374. _properties?.TryGetValue(TypeConverter.ToString(key), out descriptor);
  375. }
  376. else
  377. {
  378. _symbols?.TryGetValue((JsSymbol) key, out descriptor);
  379. }
  380. return descriptor ?? PropertyDescriptor.Undefined;
  381. }
  382. protected internal virtual void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  383. {
  384. EnsureInitialized();
  385. SetProperty(property, desc);
  386. }
  387. /// <summary>
  388. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.2
  389. /// </summary>
  390. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  391. public PropertyDescriptor GetProperty(JsValue property)
  392. {
  393. var prop = GetOwnProperty(property);
  394. if (prop != PropertyDescriptor.Undefined)
  395. {
  396. return prop;
  397. }
  398. return Prototype?.GetProperty(property) ?? PropertyDescriptor.Undefined;
  399. }
  400. public bool TryGetValue(JsValue property, out JsValue value)
  401. {
  402. value = Undefined;
  403. var desc = GetOwnProperty(property);
  404. if (desc != null && desc != PropertyDescriptor.Undefined)
  405. {
  406. if (desc == PropertyDescriptor.Undefined)
  407. {
  408. return false;
  409. }
  410. var descValue = desc.Value;
  411. if (desc.WritableSet && !ReferenceEquals(descValue, null))
  412. {
  413. value = descValue;
  414. return true;
  415. }
  416. var getter = desc.Get ?? Undefined;
  417. if (getter.IsUndefined())
  418. {
  419. value = Undefined;
  420. return false;
  421. }
  422. // if getter is not undefined it must be ICallable
  423. var callable = (ICallable) getter;
  424. value = callable.Call(this, Arguments.Empty);
  425. return true;
  426. }
  427. if (ReferenceEquals(Prototype, null))
  428. {
  429. return false;
  430. }
  431. return Prototype.TryGetValue(property, out value);
  432. }
  433. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  434. public bool Set(JsValue p, JsValue v, bool throwOnError)
  435. {
  436. if (!Set(p, v) && throwOnError)
  437. {
  438. ExceptionHelper.ThrowTypeError(_engine.Realm);
  439. }
  440. return true;
  441. }
  442. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  443. public bool Set(JsValue property, JsValue value)
  444. {
  445. if ((_type & InternalTypes.PlainObject) != 0 && property is JsString jsString)
  446. {
  447. var key = (Key) jsString.ToString();
  448. if (_properties?.TryGetValue(key, out var ownDesc) == true)
  449. {
  450. if ((ownDesc._flags & PropertyFlag.Writable) != 0)
  451. {
  452. ownDesc._value = value;
  453. return true;
  454. }
  455. }
  456. }
  457. return Set(property, value, this);
  458. }
  459. private static readonly PropertyDescriptor _marker = new(Undefined, PropertyFlag.ConfigurableEnumerableWritable);
  460. /// <summary>
  461. /// https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor
  462. /// </summary>
  463. public override bool Set(JsValue property, JsValue value, JsValue receiver)
  464. {
  465. if ((_type & InternalTypes.PlainObject) != 0 && ReferenceEquals(this, receiver) && property is JsString jsString)
  466. {
  467. var key = (Key) jsString.ToString();
  468. if (_properties?.TryGetValue(key, out var ownDesc) == true)
  469. {
  470. if ((ownDesc._flags & PropertyFlag.Writable) != 0)
  471. {
  472. ownDesc._value = value;
  473. return true;
  474. }
  475. }
  476. }
  477. return SetUnlikely(property, value, receiver);
  478. }
  479. [MethodImpl(MethodImplOptions.NoInlining)]
  480. private bool SetUnlikely(JsValue property, JsValue value, JsValue receiver)
  481. {
  482. var ownDesc = GetOwnProperty(property);
  483. if (ownDesc == PropertyDescriptor.Undefined)
  484. {
  485. var parent = GetPrototypeOf();
  486. if (parent is not null)
  487. {
  488. return parent.Set(property, value, receiver);
  489. }
  490. ownDesc = _marker;
  491. }
  492. if (ownDesc.IsDataDescriptor())
  493. {
  494. if (!ownDesc.Writable)
  495. {
  496. return false;
  497. }
  498. if (receiver is not ObjectInstance oi)
  499. {
  500. return false;
  501. }
  502. var existingDescriptor = oi.GetOwnProperty(property);
  503. if (existingDescriptor != PropertyDescriptor.Undefined)
  504. {
  505. if (existingDescriptor.IsAccessorDescriptor())
  506. {
  507. return false;
  508. }
  509. if (!existingDescriptor.Writable)
  510. {
  511. return false;
  512. }
  513. var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
  514. return oi.DefineOwnProperty(property, valueDesc);
  515. }
  516. else
  517. {
  518. return oi.CreateDataProperty(property, value);
  519. }
  520. }
  521. if (ownDesc.Set is not FunctionInstance setter)
  522. {
  523. return false;
  524. }
  525. _engine.Call(setter, receiver, new[]
  526. {
  527. value
  528. }, expression: null);
  529. return true;
  530. }
  531. /// <summary>
  532. /// Returns a Boolean value indicating whether a
  533. /// [[Put]] operation with PropertyName can be
  534. /// performed.
  535. /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.4
  536. /// </summary>
  537. public bool CanPut(JsValue property)
  538. {
  539. var desc = GetOwnProperty(property);
  540. if (desc != PropertyDescriptor.Undefined)
  541. {
  542. if (desc.IsAccessorDescriptor())
  543. {
  544. var set = desc.Set;
  545. if (ReferenceEquals(set, null) || set.IsUndefined())
  546. {
  547. return false;
  548. }
  549. return true;
  550. }
  551. return desc.Writable;
  552. }
  553. if (ReferenceEquals(Prototype, null))
  554. {
  555. return Extensible;
  556. }
  557. var inherited = Prototype.GetProperty(property);
  558. if (inherited == PropertyDescriptor.Undefined)
  559. {
  560. return Extensible;
  561. }
  562. if (inherited.IsAccessorDescriptor())
  563. {
  564. var set = inherited.Set;
  565. if (ReferenceEquals(set, null) || set.IsUndefined())
  566. {
  567. return false;
  568. }
  569. return true;
  570. }
  571. if (!Extensible)
  572. {
  573. return false;
  574. }
  575. return inherited.Writable;
  576. }
  577. /// <summary>
  578. /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
  579. /// </summary>
  580. public virtual bool HasProperty(JsValue property)
  581. {
  582. var key = TypeConverter.ToPropertyKey(property);
  583. var hasOwn = GetOwnProperty(key);
  584. if (hasOwn != PropertyDescriptor.Undefined)
  585. {
  586. return true;
  587. }
  588. var parent = GetPrototypeOf();
  589. if (parent is not null)
  590. {
  591. return parent.HasProperty(key);
  592. }
  593. return false;
  594. }
  595. /// <summary>
  596. /// https://tc39.es/ecma262/#sec-deletepropertyorthrow
  597. /// </summary>
  598. public bool DeletePropertyOrThrow(JsValue property)
  599. {
  600. if (!Delete(property))
  601. {
  602. ExceptionHelper.ThrowTypeError(_engine.Realm);
  603. }
  604. return true;
  605. }
  606. /// <summary>
  607. /// Removes the specified named own property
  608. /// from the object. The flag controls failure
  609. /// handling.
  610. /// </summary>
  611. public virtual bool Delete(JsValue property)
  612. {
  613. var desc = GetOwnProperty(property);
  614. if (desc == PropertyDescriptor.Undefined)
  615. {
  616. return true;
  617. }
  618. if (desc.Configurable)
  619. {
  620. RemoveOwnProperty(property);
  621. return true;
  622. }
  623. return false;
  624. }
  625. public bool DefinePropertyOrThrow(JsValue property, PropertyDescriptor desc)
  626. {
  627. if (!DefineOwnProperty(property, desc))
  628. {
  629. ExceptionHelper.ThrowTypeError(_engine.Realm, "Cannot redefine property: " + property);
  630. }
  631. return true;
  632. }
  633. /// <summary>
  634. /// Creates or alters the named own property to have the state described by a PropertyDescriptor.
  635. /// </summary>
  636. public virtual bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  637. {
  638. var current = GetOwnProperty(property);
  639. if (current == desc)
  640. {
  641. return true;
  642. }
  643. return ValidateAndApplyPropertyDescriptor(this, property, Extensible, desc, current);
  644. }
  645. /// <summary>
  646. /// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
  647. /// </summary>
  648. protected static bool ValidateAndApplyPropertyDescriptor(ObjectInstance? o, JsValue property, bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
  649. {
  650. var descValue = desc.Value;
  651. if (current == PropertyDescriptor.Undefined)
  652. {
  653. if (!extensible)
  654. {
  655. return false;
  656. }
  657. if (o is not null)
  658. {
  659. if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
  660. {
  661. PropertyDescriptor propertyDescriptor;
  662. if ((desc._flags & PropertyFlag.ConfigurableEnumerableWritable) == PropertyFlag.ConfigurableEnumerableWritable)
  663. {
  664. propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.ConfigurableEnumerableWritable);
  665. }
  666. else if ((desc._flags & PropertyFlag.ConfigurableEnumerableWritable) == 0)
  667. {
  668. propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.AllForbidden);
  669. }
  670. else
  671. {
  672. propertyDescriptor = new PropertyDescriptor(desc)
  673. {
  674. Value = descValue ?? Undefined
  675. };
  676. }
  677. o.SetOwnProperty(property, propertyDescriptor);
  678. }
  679. else
  680. {
  681. var descriptor = new GetSetPropertyDescriptor(desc.Get, desc.Set, PropertyFlag.None)
  682. {
  683. Enumerable = desc.Enumerable,
  684. Configurable = desc.Configurable
  685. };
  686. o.SetOwnProperty(property, descriptor);
  687. }
  688. }
  689. return true;
  690. }
  691. // Step 3
  692. var currentGet = current.Get;
  693. var currentSet = current.Set;
  694. var currentValue = current.Value;
  695. // 4. If every field in Desc is absent, return true.
  696. if ((current._flags & (PropertyFlag.ConfigurableSet | PropertyFlag.EnumerableSet | PropertyFlag.WritableSet)) == 0 &&
  697. ReferenceEquals(currentGet, null) &&
  698. ReferenceEquals(currentSet, null) &&
  699. ReferenceEquals(currentValue, null))
  700. {
  701. return true;
  702. }
  703. // Step 6
  704. var descGet = desc.Get;
  705. var descSet = desc.Set;
  706. if (
  707. current.Configurable == desc.Configurable && current.ConfigurableSet == desc.ConfigurableSet &&
  708. current.Writable == desc.Writable && current.WritableSet == desc.WritableSet &&
  709. current.Enumerable == desc.Enumerable && current.EnumerableSet == desc.EnumerableSet &&
  710. ((ReferenceEquals(currentGet, null) && ReferenceEquals(descGet, null)) || (!ReferenceEquals(currentGet, null) && !ReferenceEquals(descGet, null) && SameValue(currentGet, descGet))) &&
  711. ((ReferenceEquals(currentSet, null) && ReferenceEquals(descSet, null)) || (!ReferenceEquals(currentSet, null) && !ReferenceEquals(descSet, null) && SameValue(currentSet, descSet))) &&
  712. ((ReferenceEquals(currentValue, null) && ReferenceEquals(descValue, null)) || (!ReferenceEquals(currentValue, null) && !ReferenceEquals(descValue, null) && currentValue == descValue))
  713. )
  714. {
  715. return true;
  716. }
  717. if (!current.Configurable)
  718. {
  719. if (desc.Configurable)
  720. {
  721. return false;
  722. }
  723. if (desc.EnumerableSet && (desc.Enumerable != current.Enumerable))
  724. {
  725. return false;
  726. }
  727. }
  728. if (!desc.IsGenericDescriptor())
  729. {
  730. if (current.IsDataDescriptor() != desc.IsDataDescriptor())
  731. {
  732. if (!current.Configurable)
  733. {
  734. return false;
  735. }
  736. if (o is not null)
  737. {
  738. var flags = current.Flags & ~(PropertyFlag.Writable | PropertyFlag.WritableSet | PropertyFlag.CustomJsValue);
  739. if (current.IsDataDescriptor())
  740. {
  741. o.SetOwnProperty(property, current = new GetSetPropertyDescriptor(
  742. get: Undefined,
  743. set: Undefined,
  744. flags
  745. ));
  746. }
  747. else
  748. {
  749. o.SetOwnProperty(property, current = new PropertyDescriptor(
  750. value: Undefined,
  751. flags
  752. ));
  753. }
  754. }
  755. }
  756. else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
  757. {
  758. if (!current.Configurable)
  759. {
  760. if (!current.Writable && desc.Writable)
  761. {
  762. return false;
  763. }
  764. if (!current.Writable)
  765. {
  766. if (!ReferenceEquals(descValue, null) && !SameValue(descValue, currentValue!))
  767. {
  768. return false;
  769. }
  770. }
  771. }
  772. }
  773. else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor())
  774. {
  775. if (!current.Configurable)
  776. {
  777. if ((!ReferenceEquals(descSet, null) && !SameValue(descSet, currentSet ?? Undefined))
  778. ||
  779. (!ReferenceEquals(descGet, null) && !SameValue(descGet, currentGet ?? Undefined)))
  780. {
  781. return false;
  782. }
  783. }
  784. }
  785. }
  786. if (o is not null)
  787. {
  788. if (!ReferenceEquals(descValue, null))
  789. {
  790. current.Value = descValue;
  791. }
  792. if (desc.WritableSet)
  793. {
  794. current.Writable = desc.Writable;
  795. }
  796. if (desc.EnumerableSet)
  797. {
  798. current.Enumerable = desc.Enumerable;
  799. }
  800. if (desc.ConfigurableSet)
  801. {
  802. current.Configurable = desc.Configurable;
  803. }
  804. PropertyDescriptor? mutable = null;
  805. if (!ReferenceEquals(descGet, null))
  806. {
  807. mutable = new GetSetPropertyDescriptor(mutable ?? current);
  808. ((GetSetPropertyDescriptor) mutable).SetGet(descGet);
  809. }
  810. if (!ReferenceEquals(descSet, null))
  811. {
  812. mutable = new GetSetPropertyDescriptor(mutable ?? current);
  813. ((GetSetPropertyDescriptor) mutable).SetSet(descSet);
  814. }
  815. if (mutable != null)
  816. {
  817. // replace old with new type that supports get and set
  818. o.SetOwnProperty(property, mutable);
  819. }
  820. }
  821. return true;
  822. }
  823. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  824. protected internal void EnsureInitialized()
  825. {
  826. if (_initialized)
  827. {
  828. return;
  829. }
  830. // we need to set flag eagerly to prevent wrong recursion
  831. _initialized = true;
  832. Initialize();
  833. }
  834. protected virtual void Initialize()
  835. {
  836. }
  837. public override object ToObject()
  838. {
  839. return ToObject(new ObjectTraverseStack(_engine));
  840. }
  841. private object ToObject(ObjectTraverseStack stack)
  842. {
  843. if (this is IObjectWrapper wrapper)
  844. {
  845. return wrapper.Target;
  846. }
  847. stack.Enter(this);
  848. object? converted = null;
  849. switch (Class)
  850. {
  851. case ObjectClass.String:
  852. if (this is StringInstance stringInstance)
  853. {
  854. converted = stringInstance.StringData.ToString();
  855. }
  856. break;
  857. case ObjectClass.Date:
  858. if (this is JsDate dateInstance)
  859. {
  860. converted = dateInstance.ToDateTime();
  861. }
  862. break;
  863. case ObjectClass.Boolean:
  864. if (this is BooleanInstance booleanInstance)
  865. {
  866. converted = ((JsBoolean) booleanInstance.BooleanData)._value
  867. ? JsBoolean.BoxedTrue
  868. : JsBoolean.BoxedFalse;
  869. }
  870. break;
  871. case ObjectClass.Function:
  872. if (this is ICallable function)
  873. {
  874. converted = (Func<JsValue, JsValue[], JsValue>) function.Call;
  875. }
  876. break;
  877. case ObjectClass.Number:
  878. if (this is NumberInstance numberInstance)
  879. {
  880. converted = numberInstance.NumberData._value;
  881. }
  882. break;
  883. case ObjectClass.RegExp:
  884. if (this is JsRegExp regeExpInstance)
  885. {
  886. converted = regeExpInstance.Value;
  887. }
  888. break;
  889. case ObjectClass.Arguments:
  890. case ObjectClass.Object:
  891. if (this is JsArray arrayInstance)
  892. {
  893. var result = new object?[arrayInstance.Length];
  894. for (uint i = 0; i < result.Length; i++)
  895. {
  896. var value = arrayInstance[i];
  897. object? valueToSet = null;
  898. if (!value.IsUndefined())
  899. {
  900. valueToSet = value is ObjectInstance oi
  901. ? oi.ToObject(stack)
  902. : value.ToObject();
  903. }
  904. result[i] = valueToSet;
  905. }
  906. converted = result;
  907. break;
  908. }
  909. if (this is JsTypedArray typedArrayInstance)
  910. {
  911. converted = typedArrayInstance._arrayElementType switch
  912. {
  913. TypedArrayElementType.Int8 => typedArrayInstance.ToNativeArray<sbyte>(),
  914. TypedArrayElementType.Int16 => typedArrayInstance.ToNativeArray<short>(),
  915. TypedArrayElementType.Int32 => typedArrayInstance.ToNativeArray<int>(),
  916. TypedArrayElementType.BigInt64 => typedArrayInstance.ToNativeArray<long>(),
  917. TypedArrayElementType.Float32 => typedArrayInstance.ToNativeArray<float>(),
  918. TypedArrayElementType.Float64 => typedArrayInstance.ToNativeArray<double>(),
  919. TypedArrayElementType.Uint8 => typedArrayInstance.ToNativeArray<byte>(),
  920. TypedArrayElementType.Uint8C => typedArrayInstance.ToNativeArray<byte>(),
  921. TypedArrayElementType.Uint16 => typedArrayInstance.ToNativeArray<ushort>(),
  922. TypedArrayElementType.Uint32 => typedArrayInstance.ToNativeArray<uint>(),
  923. TypedArrayElementType.BigUint64 => typedArrayInstance.ToNativeArray<ulong>(),
  924. _ => throw new ArgumentOutOfRangeException()
  925. };
  926. break;
  927. }
  928. if (this is BigIntInstance bigIntInstance)
  929. {
  930. converted = bigIntInstance.BigIntData._value;
  931. break;
  932. }
  933. var o = _engine.Options.Interop.CreateClrObject(this);
  934. foreach (var p in GetOwnProperties())
  935. {
  936. if (!p.Value.Enumerable)
  937. {
  938. continue;
  939. }
  940. var key = p.Key.ToString();
  941. var propertyValue = Get(p.Key);
  942. var value = propertyValue is ObjectInstance oi
  943. ? oi.ToObject(stack)
  944. : propertyValue.ToObject();
  945. o.Add(key, value);
  946. }
  947. converted = o;
  948. break;
  949. default:
  950. converted = this;
  951. break;
  952. }
  953. stack.Exit();
  954. return converted!;
  955. }
  956. /// <summary>
  957. /// Handles the generic find of (callback[, thisArg])
  958. /// </summary>
  959. internal virtual bool FindWithCallback(
  960. JsValue[] arguments,
  961. out ulong index,
  962. out JsValue value,
  963. bool visitUnassigned,
  964. bool fromEnd = false)
  965. {
  966. ulong GetLength()
  967. {
  968. var descValue = Get(CommonProperties.Length);
  969. var len = TypeConverter.ToNumber(descValue);
  970. return (ulong) System.Math.Max(
  971. 0,
  972. System.Math.Min(len, ArrayOperations.MaxArrayLikeLength));
  973. }
  974. bool TryGetValue(ulong idx, out JsValue jsValue)
  975. {
  976. var property = JsString.Create(idx);
  977. var kPresent = HasProperty(property);
  978. jsValue = kPresent ? Get(property) : Undefined;
  979. return kPresent;
  980. }
  981. var length = GetLength();
  982. if (length == 0)
  983. {
  984. index = 0;
  985. value = Undefined;
  986. return false;
  987. }
  988. var callbackfn = arguments.At(0);
  989. var thisArg = arguments.At(1);
  990. var callable = GetCallable(callbackfn);
  991. var args = _engine._jsValueArrayPool.RentArray(3);
  992. args[2] = this;
  993. if (!fromEnd)
  994. {
  995. for (ulong k = 0; k < length; k++)
  996. {
  997. if (TryGetValue(k, out var kvalue) || visitUnassigned)
  998. {
  999. args[0] = kvalue;
  1000. args[1] = k;
  1001. var testResult = callable.Call(thisArg, args);
  1002. if (TypeConverter.ToBoolean(testResult))
  1003. {
  1004. index = k;
  1005. value = kvalue;
  1006. return true;
  1007. }
  1008. }
  1009. }
  1010. }
  1011. else
  1012. {
  1013. for (ulong k = length - 1; k >= 0; k--)
  1014. {
  1015. if (TryGetValue(k, out var kvalue) || visitUnassigned)
  1016. {
  1017. kvalue ??= Undefined;
  1018. args[0] = kvalue;
  1019. args[1] = k;
  1020. var testResult = callable.Call(thisArg, args);
  1021. if (TypeConverter.ToBoolean(testResult))
  1022. {
  1023. index = k;
  1024. value = kvalue;
  1025. return true;
  1026. }
  1027. }
  1028. }
  1029. }
  1030. _engine._jsValueArrayPool.ReturnArray(args);
  1031. index = 0;
  1032. value = Undefined;
  1033. return false;
  1034. }
  1035. internal ICallable GetCallable(JsValue source) => source.GetCallable(_engine.Realm);
  1036. internal bool IsConcatSpreadable
  1037. {
  1038. get
  1039. {
  1040. var spreadable = Get(GlobalSymbolRegistry.IsConcatSpreadable);
  1041. if (!spreadable.IsUndefined())
  1042. {
  1043. return TypeConverter.ToBoolean(spreadable);
  1044. }
  1045. return IsArray();
  1046. }
  1047. }
  1048. public virtual bool IsArrayLike => TryGetValue(CommonProperties.Length, out var lengthValue)
  1049. && lengthValue.IsNumber()
  1050. && ((JsNumber) lengthValue)._value >= 0;
  1051. // safe default
  1052. internal virtual bool HasOriginalIterator => false;
  1053. internal override bool IsIntegerIndexedArray => false;
  1054. public virtual uint Length => (uint) TypeConverter.ToLength(Get(CommonProperties.Length));
  1055. public virtual bool PreventExtensions()
  1056. {
  1057. Extensible = false;
  1058. return true;
  1059. }
  1060. protected internal virtual ObjectInstance? GetPrototypeOf()
  1061. {
  1062. return _prototype;
  1063. }
  1064. /// <summary>
  1065. /// https://tc39.es/ecma262/#sec-ordinarysetprototypeof
  1066. /// </summary>
  1067. public virtual bool SetPrototypeOf(JsValue value)
  1068. {
  1069. if (!value.IsObject() && !value.IsNull())
  1070. {
  1071. ExceptionHelper.ThrowArgumentException();
  1072. }
  1073. var current = _prototype ?? Null;
  1074. if (ReferenceEquals(value, current))
  1075. {
  1076. return true;
  1077. }
  1078. if (!Extensible)
  1079. {
  1080. return false;
  1081. }
  1082. if (value.IsNull())
  1083. {
  1084. _prototype = null;
  1085. return true;
  1086. }
  1087. // validate chain
  1088. var p = value as ObjectInstance;
  1089. bool done = false;
  1090. while (!done)
  1091. {
  1092. if (p is null)
  1093. {
  1094. done = true;
  1095. }
  1096. else if (ReferenceEquals(p, this))
  1097. {
  1098. return false;
  1099. }
  1100. else
  1101. {
  1102. p = p._prototype;
  1103. }
  1104. }
  1105. _prototype = value as ObjectInstance;
  1106. return true;
  1107. }
  1108. /// <summary>
  1109. /// https://tc39.es/ecma262/#sec-setfunctionname
  1110. /// </summary>
  1111. internal void SetFunctionName(JsValue name, string? prefix = null)
  1112. {
  1113. if (name is JsSymbol symbol)
  1114. {
  1115. name = symbol._value.IsUndefined()
  1116. ? JsString.Empty
  1117. : new JsString("[" + symbol._value + "]");
  1118. }
  1119. if (!string.IsNullOrWhiteSpace(prefix))
  1120. {
  1121. name = prefix + " " + name;
  1122. }
  1123. DefinePropertyOrThrow(CommonProperties.Name, new PropertyDescriptor(name, PropertyFlag.Configurable));
  1124. }
  1125. /// <summary>
  1126. /// https://tc39.es/ecma262/#sec-createmethodproperty
  1127. /// </summary>
  1128. internal virtual bool CreateMethodProperty(JsValue p, JsValue v)
  1129. {
  1130. var newDesc = new PropertyDescriptor(v, PropertyFlag.NonEnumerable);
  1131. return DefineOwnProperty(p, newDesc);
  1132. }
  1133. /// <summary>
  1134. /// https://tc39.es/ecma262/#sec-createdatapropertyorthrow
  1135. /// </summary>
  1136. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1137. public bool CreateDataProperty(JsValue p, JsValue v)
  1138. {
  1139. return DefineOwnProperty(p, new PropertyDescriptor(v, PropertyFlag.ConfigurableEnumerableWritable));
  1140. }
  1141. /// <summary>
  1142. /// https://tc39.es/ecma262/#sec-createdataproperty
  1143. /// </summary>
  1144. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1145. internal bool CreateDataPropertyOrThrow(JsValue p, JsValue v)
  1146. {
  1147. if (!CreateDataProperty(p, v))
  1148. {
  1149. ExceptionHelper.ThrowTypeError(_engine.Realm);
  1150. }
  1151. return true;
  1152. }
  1153. /// <summary>
  1154. /// https://tc39.es/ecma262/#sec-createnonenumerabledatapropertyorthrow
  1155. /// </summary>
  1156. internal void CreateNonEnumerableDataPropertyOrThrow(JsValue p, JsValue v)
  1157. {
  1158. var newDesc = new PropertyDescriptor(v, true, false, true);
  1159. DefinePropertyOrThrow(p, newDesc);
  1160. }
  1161. /// <summary>
  1162. /// https://tc39.es/ecma262/#sec-ordinaryobjectcreate
  1163. /// </summary>
  1164. internal static JsObject OrdinaryObjectCreate(Engine engine, ObjectInstance? proto)
  1165. {
  1166. var prototype = new JsObject(engine)
  1167. {
  1168. _prototype = proto
  1169. };
  1170. return prototype;
  1171. }
  1172. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1173. internal ICallable? GetMethod(JsValue property)
  1174. {
  1175. return GetMethod(_engine.Realm, this, property);
  1176. }
  1177. internal static ICallable? GetMethod(Realm realm, JsValue v, JsValue p)
  1178. {
  1179. var jsValue = v.Get(p);
  1180. if (jsValue.IsNullOrUndefined())
  1181. {
  1182. return null;
  1183. }
  1184. var callable = jsValue as ICallable;
  1185. if (callable is null)
  1186. {
  1187. ExceptionHelper.ThrowTypeError(realm, "Value returned for property '" + p + "' of object is not a function");
  1188. }
  1189. return callable;
  1190. }
  1191. internal void CopyDataProperties(
  1192. ObjectInstance target,
  1193. HashSet<JsValue>? excludedItems)
  1194. {
  1195. var keys = GetOwnPropertyKeys();
  1196. for (var i = 0; i < keys.Count; i++)
  1197. {
  1198. var key = keys[i];
  1199. if (excludedItems == null || !excludedItems.Contains(key))
  1200. {
  1201. var desc = GetOwnProperty(key);
  1202. if (desc.Enumerable)
  1203. {
  1204. target.CreateDataProperty(key, UnwrapJsValue(desc, this));
  1205. }
  1206. }
  1207. }
  1208. }
  1209. internal JsArray EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind kind)
  1210. {
  1211. var ownKeys = GetOwnPropertyKeys(Types.String);
  1212. var array = Engine.Realm.Intrinsics.Array.ArrayCreate((uint) ownKeys.Count);
  1213. uint index = 0;
  1214. for (var i = 0; i < ownKeys.Count; i++)
  1215. {
  1216. var property = ownKeys[i];
  1217. if (!property.IsString())
  1218. {
  1219. continue;
  1220. }
  1221. var desc = GetOwnProperty(property);
  1222. if (desc != PropertyDescriptor.Undefined && desc.Enumerable)
  1223. {
  1224. if (kind == EnumerableOwnPropertyNamesKind.Key)
  1225. {
  1226. array.SetIndexValue(index, property, updateLength: false);
  1227. }
  1228. else
  1229. {
  1230. var value = Get(property);
  1231. if (kind == EnumerableOwnPropertyNamesKind.Value)
  1232. {
  1233. array.SetIndexValue(index, value, updateLength: false);
  1234. }
  1235. else
  1236. {
  1237. var objectInstance = _engine.Realm.Intrinsics.Array.ArrayCreate(2);
  1238. objectInstance.SetIndexValue(0, property, updateLength: false);
  1239. objectInstance.SetIndexValue(1, value, updateLength: false);
  1240. array.SetIndexValue(index, objectInstance, updateLength: false);
  1241. }
  1242. }
  1243. index++;
  1244. }
  1245. }
  1246. array.SetLength(index);
  1247. return array;
  1248. }
  1249. internal enum EnumerableOwnPropertyNamesKind
  1250. {
  1251. Key,
  1252. Value,
  1253. KeyValue
  1254. }
  1255. internal ObjectInstance AssertThisIsObjectInstance(JsValue value, string methodName)
  1256. {
  1257. var instance = value as ObjectInstance;
  1258. if (instance is null)
  1259. {
  1260. ThrowIncompatibleReceiver(value, methodName);
  1261. }
  1262. return instance!;
  1263. }
  1264. [MethodImpl(MethodImplOptions.NoInlining)]
  1265. private void ThrowIncompatibleReceiver(JsValue value, string methodName)
  1266. {
  1267. ExceptionHelper.ThrowTypeError(_engine.Realm, $"Method {methodName} called on incompatible receiver {value}");
  1268. }
  1269. public override bool Equals(JsValue? obj)
  1270. {
  1271. return Equals(obj as ObjectInstance);
  1272. }
  1273. public bool Equals(ObjectInstance? other)
  1274. {
  1275. if (other is null)
  1276. {
  1277. return false;
  1278. }
  1279. if (ReferenceEquals(this, other))
  1280. {
  1281. return true;
  1282. }
  1283. return false;
  1284. }
  1285. internal IEnumerable<JsValue> GetKeys()
  1286. {
  1287. var visited = new HashSet<JsValue>();
  1288. foreach (var key in GetOwnPropertyKeys(Types.String))
  1289. {
  1290. var desc = GetOwnProperty(key);
  1291. if (desc != PropertyDescriptor.Undefined)
  1292. {
  1293. visited.Add(key);
  1294. if (desc.Enumerable)
  1295. {
  1296. yield return key;
  1297. }
  1298. }
  1299. }
  1300. if (Prototype is null)
  1301. {
  1302. yield break;
  1303. }
  1304. foreach (var protoKey in Prototype.GetKeys())
  1305. {
  1306. if (!visited.Contains(protoKey))
  1307. {
  1308. yield return protoKey;
  1309. }
  1310. }
  1311. }
  1312. public override string ToString()
  1313. {
  1314. return TypeConverter.ToString(this);
  1315. }
  1316. internal virtual ulong GetSmallestIndex(ulong length)
  1317. {
  1318. // there are some evil tests that iterate a lot with unshift..
  1319. if (Properties == null)
  1320. {
  1321. return 0;
  1322. }
  1323. var min = length;
  1324. foreach (var entry in GetOwnProperties())
  1325. {
  1326. if (ulong.TryParse(entry.Key.ToString(), out var index))
  1327. {
  1328. min = System.Math.Min(index, min);
  1329. }
  1330. }
  1331. if (Prototype?.Properties != null)
  1332. {
  1333. foreach (var entry in Prototype.GetOwnProperties())
  1334. {
  1335. if (ulong.TryParse(entry.Key.ToString(), out var index))
  1336. {
  1337. min = System.Math.Min(index, min);
  1338. }
  1339. }
  1340. }
  1341. return min;
  1342. }
  1343. /// <summary>
  1344. /// https://tc39.es/ecma262/#sec-invoke
  1345. /// </summary>
  1346. internal JsValue Invoke(JsValue v, JsValue p, JsValue[] arguments)
  1347. {
  1348. var func = v.GetV(_engine.Realm, p);
  1349. if (func is not ICallable callable)
  1350. {
  1351. ExceptionHelper.ThrowTypeError(_engine.Realm, "Can only invoke functions");
  1352. return default;
  1353. }
  1354. return callable.Call(v, arguments);
  1355. }
  1356. /// <summary>
  1357. /// https://tc39.es/ecma262/#sec-setintegritylevel
  1358. /// </summary>
  1359. internal bool SetIntegrityLevel(IntegrityLevel level)
  1360. {
  1361. var status = PreventExtensions();
  1362. if (!status)
  1363. {
  1364. return false;
  1365. }
  1366. var keys = GetOwnPropertyKeys();
  1367. if (level == IntegrityLevel.Sealed)
  1368. {
  1369. for (var i = 0; i < keys.Count; i++)
  1370. {
  1371. var k = keys[i];
  1372. DefinePropertyOrThrow(k, new PropertyDescriptor { Configurable = false });
  1373. }
  1374. }
  1375. else
  1376. {
  1377. for (var i = 0; i < keys.Count; i++)
  1378. {
  1379. var k = keys[i];
  1380. var currentDesc = GetOwnProperty(k);
  1381. if (currentDesc != PropertyDescriptor.Undefined)
  1382. {
  1383. PropertyDescriptor desc;
  1384. if (currentDesc.IsAccessorDescriptor())
  1385. {
  1386. desc = new PropertyDescriptor { Configurable = false };
  1387. }
  1388. else
  1389. {
  1390. desc = new PropertyDescriptor { Configurable = false, Writable = false };
  1391. }
  1392. DefinePropertyOrThrow(k, desc);
  1393. }
  1394. }
  1395. }
  1396. return true;
  1397. }
  1398. /// <summary>
  1399. /// https://tc39.es/ecma262/#sec-definefield
  1400. /// </summary>
  1401. internal static void DefineField(ObjectInstance receiver, ClassFieldDefinition fieldRecord)
  1402. {
  1403. var fieldName = fieldRecord.Name;
  1404. var initializer = fieldRecord.Initializer;
  1405. var initValue = Undefined;
  1406. if (initializer is not null)
  1407. {
  1408. initValue = receiver._engine.Call(initializer, receiver);
  1409. if (initValue is FunctionInstance functionInstance)
  1410. {
  1411. functionInstance.SetFunctionName(fieldName);
  1412. }
  1413. }
  1414. if (fieldName is PrivateName privateName)
  1415. {
  1416. receiver.PrivateFieldAdd(privateName, initValue);
  1417. }
  1418. else
  1419. {
  1420. receiver.CreateDataPropertyOrThrow(fieldName, initValue);
  1421. }
  1422. }
  1423. internal enum IntegrityLevel
  1424. {
  1425. Sealed,
  1426. Frozen
  1427. }
  1428. }
  1429. }