ES5Array.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. namespace Js
  7. {
  8. CompileAssert(sizeof(ES5Array) == sizeof(JavascriptArray));
  9. ES5ArrayType::ES5ArrayType(DynamicType* type)
  10. : DynamicType(type->GetScriptContext(), TypeIds_ES5Array, type->GetPrototype(), type->GetEntryPoint(), type->GetTypeHandler(), false, false)
  11. {
  12. }
  13. DynamicType* ES5Array::DuplicateType()
  14. {
  15. return RecyclerNew(GetScriptContext()->GetRecycler(), ES5ArrayType, this->GetDynamicType());
  16. }
  17. bool ES5Array::IsLengthWritable() const
  18. {
  19. return GetTypeHandler()->IsLengthWritable();
  20. }
  21. PropertyQueryFlags ES5Array::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
  22. {
  23. if (propertyId == PropertyIds::length)
  24. {
  25. return PropertyQueryFlags::Property_Found;
  26. }
  27. // Skip JavascriptArray override
  28. return DynamicObject::HasPropertyQuery(propertyId, info);
  29. }
  30. BOOL ES5Array::IsWritable(PropertyId propertyId)
  31. {
  32. if (propertyId == PropertyIds::length)
  33. {
  34. return IsLengthWritable();
  35. }
  36. return __super::IsWritable(propertyId);
  37. }
  38. BOOL ES5Array::SetEnumerable(PropertyId propertyId, BOOL value)
  39. {
  40. // Skip JavascriptArray override
  41. return DynamicObject::SetEnumerable(propertyId, value);
  42. }
  43. BOOL ES5Array::SetWritable(PropertyId propertyId, BOOL value)
  44. {
  45. // Skip JavascriptArray override
  46. return DynamicObject::SetWritable(propertyId, value);
  47. }
  48. BOOL ES5Array::SetConfigurable(PropertyId propertyId, BOOL value)
  49. {
  50. // Skip JavascriptArray override
  51. return DynamicObject::SetConfigurable(propertyId, value);
  52. }
  53. BOOL ES5Array::SetAttributes(PropertyId propertyId, PropertyAttributes attributes)
  54. {
  55. // Skip JavascriptArray override
  56. return DynamicObject::SetAttributes(propertyId, attributes);
  57. }
  58. PropertyQueryFlags ES5Array::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  59. {
  60. BOOL result;
  61. if (GetPropertyBuiltIns(propertyId, value, &result))
  62. {
  63. return JavascriptConversion::BooleanToPropertyQueryFlags(result);
  64. }
  65. // Skip JavascriptArray override
  66. return DynamicObject::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
  67. }
  68. PropertyQueryFlags ES5Array::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  69. {
  70. BOOL result;
  71. PropertyRecord const* propertyRecord;
  72. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  73. if (propertyRecord != nullptr && GetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, &result))
  74. {
  75. return JavascriptConversion::BooleanToPropertyQueryFlags(result);
  76. }
  77. // Skip JavascriptArray override
  78. return DynamicObject::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
  79. }
  80. bool ES5Array::GetPropertyBuiltIns(PropertyId propertyId, Var* value, BOOL* result)
  81. {
  82. if (propertyId == PropertyIds::length)
  83. {
  84. *value = JavascriptNumber::ToVar(this->GetLength(), GetScriptContext());
  85. *result = true;
  86. return true;
  87. }
  88. return false;
  89. }
  90. PropertyQueryFlags ES5Array::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  91. {
  92. return ES5Array::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
  93. }
  94. // Convert a Var to array length, throw RangeError if value is not valid for array length.
  95. uint32 ES5Array::ToLengthValue(Var value, ScriptContext* scriptContext)
  96. {
  97. if (TaggedInt::Is(value))
  98. {
  99. int32 newLen = TaggedInt::ToInt32(value);
  100. if (newLen < 0)
  101. {
  102. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  103. }
  104. return static_cast<uint32>(newLen);
  105. }
  106. else
  107. {
  108. uint32 newLen = JavascriptConversion::ToUInt32(value, scriptContext);
  109. if (newLen != JavascriptConversion::ToNumber(value, scriptContext))
  110. {
  111. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  112. }
  113. // Conversion can change the type (e.g. from String), invalidating assumptions made by the JIT
  114. scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
  115. return newLen;
  116. }
  117. }
  118. DescriptorFlags ES5Array::GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  119. {
  120. DescriptorFlags result;
  121. if (GetSetterBuiltIns(propertyId, info, &result))
  122. {
  123. return result;
  124. }
  125. return DynamicObject::GetSetter(propertyId, setterValue, info, requestContext);
  126. }
  127. DescriptorFlags ES5Array::GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  128. {
  129. DescriptorFlags result;
  130. PropertyRecord const* propertyRecord;
  131. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  132. if (propertyRecord != nullptr && GetSetterBuiltIns(propertyRecord->GetPropertyId(), info, &result))
  133. {
  134. return result;
  135. }
  136. return DynamicObject::GetSetter(propertyNameString, setterValue, info, requestContext);
  137. }
  138. bool ES5Array::GetSetterBuiltIns(PropertyId propertyId, PropertyValueInfo* info, DescriptorFlags* result)
  139. {
  140. if (propertyId == PropertyIds::length)
  141. {
  142. PropertyValueInfo::SetNoCache(info, this);
  143. *result = IsLengthWritable() ? WritableData : Data;
  144. return true;
  145. }
  146. return false;
  147. }
  148. BOOL ES5Array::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags propertyOperationFlags, PropertyValueInfo* info)
  149. {
  150. BOOL result;
  151. if (SetPropertyBuiltIns(propertyId, value, propertyOperationFlags, &result))
  152. {
  153. return result;
  154. }
  155. return __super::SetProperty(propertyId, value, propertyOperationFlags, info);
  156. }
  157. BOOL ES5Array::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags propertyOperationFlags, PropertyValueInfo* info)
  158. {
  159. BOOL result;
  160. PropertyRecord const* propertyRecord;
  161. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  162. if (propertyRecord != nullptr && SetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, propertyOperationFlags, &result))
  163. {
  164. return result;
  165. }
  166. return __super::SetProperty(propertyNameString, value, propertyOperationFlags, info);
  167. }
  168. bool ES5Array::SetPropertyBuiltIns(PropertyId propertyId, Var value, PropertyOperationFlags propertyOperationFlags, BOOL* result)
  169. {
  170. ScriptContext* scriptContext = GetScriptContext();
  171. if (propertyId == PropertyIds::length)
  172. {
  173. if (!GetTypeHandler()->IsLengthWritable())
  174. {
  175. *result = false; // reject
  176. return true;
  177. }
  178. uint32 newLen = ToLengthValue(value, scriptContext);
  179. uint32 assignedLen = GetTypeHandler()->SetLength(this, newLen, propertyOperationFlags);
  180. if (newLen != assignedLen)
  181. {
  182. scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
  183. }
  184. *result = true;
  185. return true;
  186. }
  187. return false;
  188. }
  189. BOOL ES5Array::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
  190. {
  191. if (propertyId == PropertyIds::length)
  192. {
  193. Assert(attributes == PropertyWritable);
  194. Assert(IsWritable(propertyId) && !IsConfigurable(propertyId) && !IsEnumerable(propertyId));
  195. uint32 newLen = ToLengthValue(value, GetScriptContext());
  196. GetTypeHandler()->SetLength(this, newLen, PropertyOperation_None);
  197. return true;
  198. }
  199. return __super::SetPropertyWithAttributes(propertyId, value, attributes, info, flags, possibleSideEffects);
  200. }
  201. BOOL ES5Array::DeleteItem(uint32 index, PropertyOperationFlags flags)
  202. {
  203. // Skip JavascriptArray override
  204. return DynamicObject::DeleteItem(index, flags);
  205. }
  206. PropertyQueryFlags ES5Array::HasItemQuery(uint32 index)
  207. {
  208. // Skip JavascriptArray override
  209. return DynamicObject::HasItemQuery(index);
  210. }
  211. PropertyQueryFlags ES5Array::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
  212. {
  213. // Skip JavascriptArray override
  214. return DynamicObject::GetItemQuery(originalInstance, index, value, requestContext);
  215. }
  216. PropertyQueryFlags ES5Array::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext)
  217. {
  218. // Skip JavascriptArray override
  219. return DynamicObject::GetItemReferenceQuery(originalInstance, index, value, requestContext);
  220. }
  221. DescriptorFlags ES5Array::GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext)
  222. {
  223. // Skip JavascriptArray override
  224. return DynamicObject::GetItemSetter(index, setterValue, requestContext);
  225. }
  226. BOOL ES5Array::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
  227. {
  228. // Skip JavascriptArray override
  229. return DynamicObject::SetItem(index, value, flags);
  230. }
  231. BOOL ES5Array::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
  232. {
  233. // Skip JavascriptArray override
  234. return DynamicObject::SetAccessors(propertyId, getter, setter, flags);
  235. }
  236. BOOL ES5Array::PreventExtensions()
  237. {
  238. // Skip JavascriptArray override
  239. return DynamicObject::PreventExtensions();
  240. }
  241. BOOL ES5Array::Seal()
  242. {
  243. // Skip JavascriptArray override
  244. return DynamicObject::Seal();
  245. }
  246. BOOL ES5Array::Freeze()
  247. {
  248. // Skip JavascriptArray override
  249. return DynamicObject::Freeze();
  250. }
  251. BOOL ES5Array::GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, EnumeratorCache * enumeratorCache)
  252. {
  253. return enumerator->Initialize(nullptr, this, this, flags, requestContext, enumeratorCache);
  254. }
  255. JavascriptEnumerator * ES5Array::GetIndexEnumerator(EnumeratorFlags flags, ScriptContext* requestContext)
  256. {
  257. // ES5Array does not support compat mode, ignore preferSnapshotSemantics
  258. return RecyclerNew(GetScriptContext()->GetRecycler(), ES5ArrayIndexEnumerator, this, flags, requestContext);
  259. }
  260. BOOL ES5Array::IsItemEnumerable(uint32 index)
  261. {
  262. return GetTypeHandler()->IsItemEnumerable(this, index);
  263. }
  264. BOOL ES5Array::SetItemWithAttributes(uint32 index, Var value, PropertyAttributes attributes)
  265. {
  266. return GetTypeHandler()->SetItemWithAttributes(this, index, value, attributes);
  267. }
  268. BOOL ES5Array::SetItemAttributes(uint32 index, PropertyAttributes attributes)
  269. {
  270. return GetTypeHandler()->SetItemAttributes(this, index, attributes);
  271. }
  272. BOOL ES5Array::SetItemAccessors(uint32 index, Var getter, Var setter)
  273. {
  274. return GetTypeHandler()->SetItemAccessors(this, index, getter, setter);
  275. }
  276. BOOL ES5Array::IsObjectArrayFrozen()
  277. {
  278. return GetTypeHandler()->IsObjectArrayFrozen(this);
  279. }
  280. BOOL ES5Array::IsValidDescriptorToken(void * descriptorValidationToken) const
  281. {
  282. return GetTypeHandler()->IsValidDescriptorToken(descriptorValidationToken);
  283. }
  284. uint32 ES5Array::GetNextDescriptor(uint32 key, IndexPropertyDescriptor** descriptor, void ** descriptorValidationToken)
  285. {
  286. return GetTypeHandler()->GetNextDescriptor(key, descriptor, descriptorValidationToken);
  287. }
  288. BOOL ES5Array::GetDescriptor(uint32 index, Js::IndexPropertyDescriptor **ppDescriptor)
  289. {
  290. return GetTypeHandler()->GetDescriptor(index, ppDescriptor);
  291. }
  292. #if ENABLE_TTD
  293. void ES5Array::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  294. {
  295. this->JavascriptArray::MarkVisitKindSpecificPtrs(extractor);
  296. uint32 length = this->GetLength();
  297. uint32 descriptorIndex = Js::JavascriptArray::InvalidIndex;
  298. IndexPropertyDescriptor* descriptor = nullptr;
  299. void* descriptorValidationToken = nullptr;
  300. do
  301. {
  302. descriptorIndex = this->GetNextDescriptor(descriptorIndex, &descriptor, &descriptorValidationToken);
  303. if(descriptorIndex == Js::JavascriptArray::InvalidIndex || descriptorIndex >= length)
  304. {
  305. break;
  306. }
  307. if((descriptor->Attributes & PropertyDeleted) != PropertyDeleted)
  308. {
  309. if(descriptor->Getter != nullptr)
  310. {
  311. extractor->MarkVisitVar(descriptor->Getter);
  312. }
  313. if(descriptor->Setter != nullptr)
  314. {
  315. extractor->MarkVisitVar(descriptor->Setter);
  316. }
  317. }
  318. } while(true);
  319. }
  320. TTD::NSSnapObjects::SnapObjectType ES5Array::GetSnapTag_TTD() const
  321. {
  322. return TTD::NSSnapObjects::SnapObjectType::SnapES5ArrayObject;
  323. }
  324. void ES5Array::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  325. {
  326. TTD::NSSnapObjects::SnapArrayInfo<TTD::TTDVar>* sai = TTD::NSSnapObjects::ExtractArrayValues<TTD::TTDVar>(this, alloc);
  327. TTD::NSSnapObjects::SnapES5ArrayInfo* es5ArrayInfo = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapES5ArrayInfo>();
  328. //
  329. //TODO: reserving memory for entire length might be a problem if we have very large sparse arrays.
  330. //
  331. uint32 length = this->GetLength();
  332. es5ArrayInfo->IsLengthWritable = this->IsLengthWritable();
  333. es5ArrayInfo->GetterSetterCount = 0;
  334. es5ArrayInfo->GetterSetterEntries = alloc.SlabReserveArraySpace<TTD::NSSnapObjects::SnapES5ArrayGetterSetterEntry>(length + 1); //ensure we don't do a 0 reserve
  335. uint32 descriptorIndex = Js::JavascriptArray::InvalidIndex;
  336. IndexPropertyDescriptor* descriptor = nullptr;
  337. void* descriptorValidationToken = nullptr;
  338. do
  339. {
  340. descriptorIndex = this->GetNextDescriptor(descriptorIndex, &descriptor, &descriptorValidationToken);
  341. if(descriptorIndex == Js::JavascriptArray::InvalidIndex || descriptorIndex >= length)
  342. {
  343. break;
  344. }
  345. if((descriptor->Attributes & PropertyDeleted) != PropertyDeleted)
  346. {
  347. TTD::NSSnapObjects::SnapES5ArrayGetterSetterEntry* entry = es5ArrayInfo->GetterSetterEntries + es5ArrayInfo->GetterSetterCount;
  348. es5ArrayInfo->GetterSetterCount++;
  349. entry->Index = (uint32)descriptorIndex;
  350. entry->Attributes = descriptor->Attributes;
  351. entry->Getter = nullptr;
  352. if(descriptor->Getter != nullptr)
  353. {
  354. entry->Getter = descriptor->Getter;
  355. }
  356. entry->Setter = nullptr;
  357. if(descriptor->Setter != nullptr)
  358. {
  359. entry->Setter = descriptor->Setter;
  360. }
  361. }
  362. } while(true);
  363. if(es5ArrayInfo->GetterSetterCount != 0)
  364. {
  365. alloc.SlabCommitArraySpace<TTD::NSSnapObjects::SnapES5ArrayGetterSetterEntry>(es5ArrayInfo->GetterSetterCount, length + 1);
  366. }
  367. else
  368. {
  369. alloc.SlabAbortArraySpace<TTD::NSSnapObjects::SnapES5ArrayGetterSetterEntry>(length + 1);
  370. es5ArrayInfo->GetterSetterEntries = nullptr;
  371. }
  372. es5ArrayInfo->BasicArrayData = sai;
  373. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapES5ArrayInfo*, TTD::NSSnapObjects::SnapObjectType::SnapES5ArrayObject>(objData, es5ArrayInfo);
  374. }
  375. #endif
  376. }