ArrayBuffer.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  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. bool ArrayBufferBase::Is(Var value)
  9. {
  10. return ArrayBuffer::Is(value) || SharedArrayBuffer::Is(value);
  11. }
  12. ArrayBufferBase* ArrayBufferBase::FromVar(Var value)
  13. {
  14. Assert(ArrayBufferBase::Is(value));
  15. return static_cast<ArrayBuffer *> (value);
  16. }
  17. ArrayBuffer* ArrayBuffer::NewFromDetachedState(DetachedStateBase* state, JavascriptLibrary *library)
  18. {
  19. ArrayBufferDetachedStateBase* arrayBufferState = (ArrayBufferDetachedStateBase *)state;
  20. ArrayBuffer *toReturn = nullptr;
  21. switch (arrayBufferState->allocationType)
  22. {
  23. case ArrayBufferAllocationType::CoTask:
  24. toReturn = library->CreateProjectionArraybuffer(arrayBufferState->buffer, arrayBufferState->bufferLength);
  25. break;
  26. case ArrayBufferAllocationType::Heap:
  27. case ArrayBufferAllocationType::MemAlloc:
  28. toReturn = library->CreateArrayBuffer(arrayBufferState->buffer, arrayBufferState->bufferLength);
  29. break;
  30. default:
  31. AssertMsg(false, "Unknown allocationType of ArrayBufferDetachedStateBase ");
  32. }
  33. return toReturn;
  34. }
  35. void ArrayBuffer::DetachBufferFromParent(ArrayBufferParent* parent)
  36. {
  37. if (parent == nullptr)
  38. {
  39. return;
  40. }
  41. switch (JavascriptOperators::GetTypeId(parent))
  42. {
  43. case TypeIds_Int8Array:
  44. if (Int8VirtualArray::Is(parent))
  45. {
  46. if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(parent))
  47. {
  48. VirtualTableInfo<Int8Array>::SetVirtualTable(parent);
  49. }
  50. else
  51. {
  52. Assert(VirtualTableInfo<CrossSiteObject<Int8VirtualArray>>::HasVirtualTable(parent));
  53. VirtualTableInfo<CrossSiteObject<Int8Array>>::SetVirtualTable(parent);
  54. }
  55. }
  56. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  57. break;
  58. case TypeIds_Uint8Array:
  59. if (Uint8VirtualArray::Is(parent))
  60. {
  61. if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(parent))
  62. {
  63. VirtualTableInfo<Uint8Array>::SetVirtualTable(parent);
  64. }
  65. else
  66. {
  67. Assert(VirtualTableInfo<CrossSiteObject<Uint8VirtualArray>>::HasVirtualTable(parent));
  68. VirtualTableInfo<CrossSiteObject<Uint8Array>>::SetVirtualTable(parent);
  69. }
  70. }
  71. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  72. break;
  73. case TypeIds_Uint8ClampedArray:
  74. if (Uint8ClampedVirtualArray::Is(parent))
  75. {
  76. if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(parent))
  77. {
  78. VirtualTableInfo<Uint8ClampedArray>::SetVirtualTable(parent);
  79. }
  80. else
  81. {
  82. Assert(VirtualTableInfo<CrossSiteObject<Uint8ClampedVirtualArray>>::HasVirtualTable(parent));
  83. VirtualTableInfo<CrossSiteObject<Uint8ClampedArray>>::SetVirtualTable(parent);
  84. }
  85. }
  86. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  87. break;
  88. case TypeIds_Int16Array:
  89. if (Int16VirtualArray::Is(parent))
  90. {
  91. if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(parent))
  92. {
  93. VirtualTableInfo<Int16Array>::SetVirtualTable(parent);
  94. }
  95. else
  96. {
  97. Assert(VirtualTableInfo<CrossSiteObject<Int16VirtualArray>>::HasVirtualTable(parent));
  98. VirtualTableInfo<CrossSiteObject<Int16Array>>::SetVirtualTable(parent);
  99. }
  100. }
  101. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  102. break;
  103. case TypeIds_Uint16Array:
  104. if (Uint16VirtualArray::Is(parent))
  105. {
  106. if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(parent))
  107. {
  108. VirtualTableInfo<Uint16Array>::SetVirtualTable(parent);
  109. }
  110. else
  111. {
  112. Assert(VirtualTableInfo<CrossSiteObject<Uint16VirtualArray>>::HasVirtualTable(parent));
  113. VirtualTableInfo<CrossSiteObject<Uint16Array>>::SetVirtualTable(parent);
  114. }
  115. }
  116. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  117. break;
  118. case TypeIds_Int32Array:
  119. if (Int32VirtualArray::Is(parent))
  120. {
  121. if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(parent))
  122. {
  123. VirtualTableInfo<Int32Array>::SetVirtualTable(parent);
  124. }
  125. else
  126. {
  127. Assert(VirtualTableInfo<CrossSiteObject<Int32VirtualArray>>::HasVirtualTable(parent));
  128. VirtualTableInfo<CrossSiteObject<Int32Array>>::SetVirtualTable(parent);
  129. }
  130. }
  131. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  132. break;
  133. case TypeIds_Uint32Array:
  134. if (Uint32VirtualArray::Is(parent))
  135. {
  136. if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(parent))
  137. {
  138. VirtualTableInfo<Uint32Array>::SetVirtualTable(parent);
  139. }
  140. else
  141. {
  142. Assert(VirtualTableInfo<CrossSiteObject<Uint32VirtualArray>>::HasVirtualTable(parent));
  143. VirtualTableInfo<CrossSiteObject<Uint32Array>>::SetVirtualTable(parent);
  144. }
  145. }
  146. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  147. break;
  148. case TypeIds_Float32Array:
  149. if (Float32VirtualArray::Is(parent))
  150. {
  151. if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(parent))
  152. {
  153. VirtualTableInfo<Float32Array>::SetVirtualTable(parent);
  154. }
  155. else
  156. {
  157. Assert(VirtualTableInfo<CrossSiteObject<Float32VirtualArray>>::HasVirtualTable(parent));
  158. VirtualTableInfo<CrossSiteObject<Float32Array>>::SetVirtualTable(parent);
  159. }
  160. }
  161. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  162. break;
  163. case TypeIds_Float64Array:
  164. if (Float64VirtualArray::Is(parent))
  165. {
  166. if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(parent))
  167. {
  168. VirtualTableInfo<Float64Array>::SetVirtualTable(parent);
  169. }
  170. else
  171. {
  172. Assert(VirtualTableInfo<CrossSiteObject<Float64VirtualArray>>::HasVirtualTable(parent));
  173. VirtualTableInfo<CrossSiteObject<Float64Array>>::SetVirtualTable(parent);
  174. }
  175. }
  176. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  177. break;
  178. case TypeIds_Int64Array:
  179. case TypeIds_Uint64Array:
  180. case TypeIds_CharArray:
  181. case TypeIds_BoolArray:
  182. TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
  183. break;
  184. case TypeIds_DataView:
  185. DataView::FromVar(parent)->ClearLengthAndBufferOnDetach();
  186. break;
  187. default:
  188. AssertMsg(false, "We need an explicit case for any parent of ArrayBuffer.");
  189. break;
  190. }
  191. }
  192. ArrayBufferDetachedStateBase* ArrayBuffer::DetachAndGetState()
  193. {
  194. Assert(!this->isDetached);
  195. AutoPtr<ArrayBufferDetachedStateBase> arrayBufferState(this->CreateDetachedState(this->buffer, this->bufferLength));
  196. this->buffer = nullptr;
  197. this->bufferLength = 0;
  198. this->isDetached = true;
  199. if (this->primaryParent != nullptr && this->primaryParent->Get() == nullptr)
  200. {
  201. this->primaryParent = nullptr;
  202. }
  203. if (this->primaryParent != nullptr)
  204. {
  205. this->DetachBufferFromParent(this->primaryParent->Get());
  206. }
  207. if (this->otherParents != nullptr)
  208. {
  209. this->otherParents->Map([&](RecyclerWeakReference<ArrayBufferParent>* item)
  210. {
  211. this->DetachBufferFromParent(item->Get());
  212. });
  213. }
  214. return arrayBufferState.Detach();
  215. }
  216. void ArrayBuffer::AddParent(ArrayBufferParent* parent)
  217. {
  218. if (this->primaryParent == nullptr || this->primaryParent->Get() == nullptr)
  219. {
  220. this->primaryParent = this->GetRecycler()->CreateWeakReferenceHandle(parent);
  221. }
  222. else
  223. {
  224. if (this->otherParents == nullptr)
  225. {
  226. this->otherParents = RecyclerNew(this->GetRecycler(), OtherParents, this->GetRecycler());
  227. }
  228. if (this->otherParents->increasedCount >= ParentsCleanupThreshold)
  229. {
  230. auto iter = this->otherParents->GetEditingIterator();
  231. while (iter.Next())
  232. {
  233. if (iter.Data()->Get() == nullptr)
  234. {
  235. iter.RemoveCurrent();
  236. }
  237. }
  238. this->otherParents->increasedCount = 0;
  239. }
  240. this->otherParents->PrependNode(this->GetRecycler()->CreateWeakReferenceHandle(parent));
  241. this->otherParents->increasedCount++;
  242. }
  243. }
  244. uint32 ArrayBuffer::ToIndex(Var value, int32 errorCode, ScriptContext *scriptContext, uint32 MaxAllowedLength, bool checkSameValueZero)
  245. {
  246. if (JavascriptOperators::IsUndefined(value))
  247. {
  248. return 0;
  249. }
  250. if (TaggedInt::Is(value))
  251. {
  252. int64 index = TaggedInt::ToInt64(value);
  253. if (index < 0 || index >(int64)MaxAllowedLength)
  254. {
  255. JavascriptError::ThrowRangeError(scriptContext, errorCode);
  256. }
  257. return (uint32)index;
  258. }
  259. // Slower path
  260. double d = JavascriptConversion::ToInteger(value, scriptContext);
  261. if (d < 0.0 || d >(double)MaxAllowedLength)
  262. {
  263. JavascriptError::ThrowRangeError(scriptContext, errorCode);
  264. }
  265. if (checkSameValueZero)
  266. {
  267. Var integerIndex = JavascriptNumber::ToVarNoCheck(d, scriptContext);
  268. Var index = JavascriptNumber::ToVar(JavascriptConversion::ToLength(integerIndex, scriptContext), scriptContext);
  269. if (!JavascriptConversion::SameValueZero(integerIndex, index))
  270. {
  271. JavascriptError::ThrowRangeError(scriptContext, errorCode);
  272. }
  273. }
  274. return (uint32)d;
  275. }
  276. Var ArrayBuffer::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  277. {
  278. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  279. ARGUMENTS(args, callInfo);
  280. ScriptContext* scriptContext = function->GetScriptContext();
  281. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  282. Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
  283. bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
  284. Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr);
  285. if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget)))
  286. {
  287. JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, _u("ArrayBuffer"));
  288. }
  289. uint32 byteLength = 0;
  290. if (args.Info.Count > 1)
  291. {
  292. byteLength = ToIndex(args[1], JSERR_ArrayLengthConstructIncorrect, scriptContext, MaxArrayBufferLength);
  293. }
  294. RecyclableObject* newArr = scriptContext->GetLibrary()->CreateArrayBuffer(byteLength);
  295. Assert(ArrayBuffer::Is(newArr));
  296. if (byteLength > 0 && !ArrayBuffer::FromVar(newArr)->GetByteLength())
  297. {
  298. JavascriptError::ThrowRangeError(scriptContext, JSERR_FunctionArgument_Invalid);
  299. }
  300. #if ENABLE_DEBUG_CONFIG_OPTIONS
  301. if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
  302. {
  303. newArr = Js::JavascriptProxy::AutoProxyWrapper(newArr);
  304. }
  305. #endif
  306. return isCtorSuperCall ?
  307. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), newArr, nullptr, scriptContext) :
  308. newArr;
  309. }
  310. // ArrayBuffer.prototype.byteLength as described in ES6 draft #20 section 24.1.4.1
  311. Var ArrayBuffer::EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...)
  312. {
  313. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  314. ARGUMENTS(args, callInfo);
  315. ScriptContext* scriptContext = function->GetScriptContext();
  316. Assert(!(callInfo.Flags & CallFlags_New));
  317. if (args.Info.Count == 0 || !ArrayBuffer::Is(args[0]))
  318. {
  319. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedArrayBufferObject);
  320. }
  321. ArrayBuffer* arrayBuffer = ArrayBuffer::FromVar(args[0]);
  322. if (arrayBuffer->IsDetached())
  323. {
  324. return JavascriptNumber::ToVar(0, scriptContext);
  325. }
  326. return JavascriptNumber::ToVar(arrayBuffer->GetByteLength(), scriptContext);
  327. }
  328. // ArrayBuffer.isView as described in ES6 draft #20 section 24.1.3.1
  329. Var ArrayBuffer::EntryIsView(RecyclableObject* function, CallInfo callInfo, ...)
  330. {
  331. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  332. ARGUMENTS(args, callInfo);
  333. Assert(!(callInfo.Flags & CallFlags_New));
  334. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  335. JavascriptLibrary* library = function->GetScriptContext()->GetLibrary();
  336. Var arg = library->GetUndefined();
  337. if (args.Info.Count > 1)
  338. {
  339. arg = args[1];
  340. }
  341. // Only DataView or any TypedArray objects have [[ViewedArrayBuffer]] internal slots
  342. if (DataView::Is(arg) || TypedArrayBase::Is(arg))
  343. {
  344. return library->GetTrue();
  345. }
  346. return library->GetFalse();
  347. }
  348. // ArrayBuffer.transfer as described in Luke Wagner's proposal: https://gist.github.com/lukewagner/2735af7eea411e18cf20
  349. Var ArrayBuffer::EntryTransfer(RecyclableObject* function, CallInfo callInfo, ...)
  350. {
  351. ScriptContext* scriptContext = function->GetScriptContext();
  352. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  353. ARGUMENTS(args, callInfo);
  354. Assert(!(callInfo.Flags & CallFlags_New));
  355. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayBuffer_Constructor_transfer);
  356. if (args.Info.Count < 2 || !ArrayBuffer::Is(args[1]))
  357. {
  358. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedArrayBufferObject);
  359. }
  360. ArrayBuffer* arrayBuffer = ArrayBuffer::FromVar(args[1]);
  361. if (arrayBuffer->IsDetached())
  362. {
  363. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("ArrayBuffer.transfer"));
  364. }
  365. uint32 newBufferLength = arrayBuffer->bufferLength;
  366. if (args.Info.Count >= 3)
  367. {
  368. newBufferLength = ToIndex(args[2], JSERR_ArrayLengthConstructIncorrect, scriptContext, MaxArrayBufferLength);
  369. // ToIndex above can call user script (valueOf) which can detach the buffer
  370. if (arrayBuffer->IsDetached())
  371. {
  372. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("ArrayBuffer.transfer"));
  373. }
  374. }
  375. return arrayBuffer->TransferInternal(newBufferLength);
  376. }
  377. // ArrayBuffer.prototype.slice as described in ES6 draft #19 section 24.1.4.3.
  378. Var ArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
  379. {
  380. ScriptContext* scriptContext = function->GetScriptContext();
  381. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  382. ARGUMENTS(args, callInfo);
  383. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  384. Assert(!(callInfo.Flags & CallFlags_New));
  385. if (!ArrayBuffer::Is(args[0]))
  386. {
  387. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedArrayBufferObject);
  388. }
  389. JavascriptLibrary* library = scriptContext->GetLibrary();
  390. ArrayBuffer* arrayBuffer = ArrayBuffer::FromVar(args[0]);
  391. if (arrayBuffer->IsDetached()) // 24.1.4.3: 5. If IsDetachedBuffer(O) is true, then throw a TypeError exception.
  392. {
  393. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("ArrayBuffer.prototype.slice"));
  394. }
  395. int64 len = arrayBuffer->bufferLength;
  396. int64 start = 0, end = 0;
  397. int64 newLen;
  398. // If no start or end arguments, use the entire length
  399. if (args.Info.Count < 2)
  400. {
  401. newLen = len;
  402. }
  403. else
  404. {
  405. start = JavascriptArray::GetIndexFromVar(args[1], len, scriptContext);
  406. // If no end argument, use length as the end
  407. if (args.Info.Count < 3 || args[2] == library->GetUndefined())
  408. {
  409. end = len;
  410. }
  411. else
  412. {
  413. end = JavascriptArray::GetIndexFromVar(args[2], len, scriptContext);
  414. }
  415. newLen = end > start ? end - start : 0;
  416. }
  417. // We can't have allocated an ArrayBuffer with byteLength > MaxArrayBufferLength.
  418. // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength.
  419. // Therefore, should be safe to cast down newLen to uint32.
  420. // TODO: If we ever support allocating ArrayBuffer with byteLength > MaxArrayBufferLength we may need to review this math.
  421. Assert(newLen < MaxArrayBufferLength);
  422. uint32 byteLength = static_cast<uint32>(newLen);
  423. ArrayBuffer* newBuffer = nullptr;
  424. if (scriptContext->GetConfig()->IsES6SpeciesEnabled())
  425. {
  426. Var constructorVar = JavascriptOperators::SpeciesConstructor(arrayBuffer, scriptContext->GetLibrary()->GetArrayBufferConstructor(), scriptContext);
  427. JavascriptFunction* constructor = JavascriptFunction::FromVar(constructorVar);
  428. Js::Var constructorArgs[] = {constructor, JavascriptNumber::ToVar(byteLength, scriptContext)};
  429. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  430. Js::Var newVar = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
  431. if (!ArrayBuffer::Is(newVar)) // 24.1.4.3: 19.If new does not have an [[ArrayBufferData]] internal slot throw a TypeError exception.
  432. {
  433. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedArrayBufferObject);
  434. }
  435. newBuffer = ArrayBuffer::FromVar(newVar);
  436. if (newBuffer->IsDetached()) // 24.1.4.3: 21. If IsDetachedBuffer(new) is true, then throw a TypeError exception.
  437. {
  438. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("ArrayBuffer.prototype.slice"));
  439. }
  440. if (newBuffer == arrayBuffer) // 24.1.4.3: 22. If SameValue(new, O) is true, then throw a TypeError exception.
  441. {
  442. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedArrayBufferObject);
  443. }
  444. if (newBuffer->bufferLength < byteLength) // 24.1.4.3: 23.If the value of new's [[ArrayBufferByteLength]] internal slot < newLen, then throw a TypeError exception.
  445. {
  446. JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("ArrayBuffer.prototype.slice"));
  447. }
  448. }
  449. else
  450. {
  451. newBuffer = library->CreateArrayBuffer(byteLength);
  452. }
  453. Assert(newBuffer);
  454. Assert(newBuffer->bufferLength >= byteLength);
  455. if (arrayBuffer->IsDetached()) // 24.1.4.3: 24. NOTE: Side-effects of the above steps may have detached O. 25. If IsDetachedBuffer(O) is true, then throw a TypeError exception.
  456. {
  457. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("ArrayBuffer.prototype.slice"));
  458. }
  459. // Don't bother doing memcpy if we aren't copying any elements
  460. if (byteLength > 0)
  461. {
  462. AssertMsg(arrayBuffer->buffer != nullptr, "buffer must not be null when we copy from it");
  463. js_memcpy_s(newBuffer->buffer, byteLength, arrayBuffer->buffer + start, byteLength);
  464. }
  465. return newBuffer;
  466. }
  467. Var ArrayBuffer::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  468. {
  469. ARGUMENTS(args, callInfo);
  470. Assert(args.Info.Count > 0);
  471. return args[0];
  472. }
  473. ArrayBuffer* ArrayBuffer::FromVar(Var aValue)
  474. {
  475. AssertMsg(Is(aValue), "var must be an ArrayBuffer");
  476. return static_cast<ArrayBuffer *>(RecyclableObject::FromVar(aValue));
  477. }
  478. bool ArrayBuffer::Is(Var aValue)
  479. {
  480. return JavascriptOperators::GetTypeId(aValue) == TypeIds_ArrayBuffer;
  481. }
  482. template <class Allocator>
  483. ArrayBuffer::ArrayBuffer(uint32 length, DynamicType * type, Allocator allocator) :
  484. ArrayBufferBase(type)
  485. {
  486. buffer = nullptr;
  487. bufferLength = 0;
  488. if (length > MaxArrayBufferLength)
  489. {
  490. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
  491. }
  492. else if (length > 0)
  493. {
  494. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  495. if (recycler->ReportExternalMemoryAllocation(length))
  496. {
  497. buffer = (BYTE*)allocator(length);
  498. if (buffer == nullptr)
  499. {
  500. recycler->ReportExternalMemoryFree(length);
  501. }
  502. }
  503. if (buffer == nullptr)
  504. {
  505. recycler->CollectNow<CollectOnTypedArrayAllocation>();
  506. if (recycler->ReportExternalMemoryAllocation(length))
  507. {
  508. buffer = (BYTE*)allocator(length);
  509. if (buffer == nullptr)
  510. {
  511. recycler->ReportExternalMemoryFailure(length);
  512. }
  513. }
  514. else
  515. {
  516. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  517. }
  518. }
  519. if (buffer != nullptr)
  520. {
  521. bufferLength = length;
  522. ZeroMemory(buffer, bufferLength);
  523. }
  524. }
  525. }
  526. ArrayBuffer::ArrayBuffer(byte* buffer, uint32 length, DynamicType * type) :
  527. buffer(buffer), bufferLength(length), ArrayBufferBase(type)
  528. {
  529. if (length > MaxArrayBufferLength)
  530. {
  531. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
  532. }
  533. }
  534. BOOL ArrayBuffer::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  535. {
  536. stringBuilder->AppendCppLiteral(_u("Object, (ArrayBuffer)"));
  537. return TRUE;
  538. }
  539. BOOL ArrayBuffer::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  540. {
  541. stringBuilder->AppendCppLiteral(_u("[object ArrayBuffer]"));
  542. return TRUE;
  543. }
  544. #if ENABLE_TTD
  545. void ArrayBufferParent::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  546. {
  547. extractor->MarkVisitVar(this->arrayBuffer);
  548. }
  549. void ArrayBufferParent::ProcessCorePaths()
  550. {
  551. this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->arrayBuffer, _u("!buffer"));
  552. }
  553. #endif
  554. JavascriptArrayBuffer::JavascriptArrayBuffer(uint32 length, DynamicType * type) :
  555. ArrayBuffer(length, type, IsValidVirtualBufferLength(length) ? AsmJsVirtualAllocator : malloc)
  556. {
  557. }
  558. JavascriptArrayBuffer::JavascriptArrayBuffer(byte* buffer, uint32 length, DynamicType * type) :
  559. ArrayBuffer(buffer, length, type)
  560. {
  561. }
  562. JavascriptArrayBuffer::JavascriptArrayBuffer(DynamicType * type) : ArrayBuffer(0, type, malloc)
  563. {
  564. }
  565. JavascriptArrayBuffer* JavascriptArrayBuffer::Create(uint32 length, DynamicType * type)
  566. {
  567. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  568. JavascriptArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptArrayBuffer, length, type);
  569. Assert(result);
  570. recycler->AddExternalMemoryUsage(length);
  571. return result;
  572. }
  573. JavascriptArrayBuffer* JavascriptArrayBuffer::Create(byte* buffer, uint32 length, DynamicType * type)
  574. {
  575. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  576. JavascriptArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptArrayBuffer, buffer, length, type);
  577. Assert(result);
  578. recycler->AddExternalMemoryUsage(length);
  579. return result;
  580. }
  581. ArrayBufferDetachedStateBase* JavascriptArrayBuffer::CreateDetachedState(BYTE* buffer, uint32 bufferLength)
  582. {
  583. #if ENABLE_FAST_ARRAYBUFFER
  584. if (IsValidVirtualBufferLength(bufferLength))
  585. {
  586. return HeapNew(ArrayBufferDetachedState<FreeFn>, buffer, bufferLength, FreeMemAlloc, ArrayBufferAllocationType::MemAlloc);
  587. }
  588. else
  589. {
  590. return HeapNew(ArrayBufferDetachedState<FreeFn>, buffer, bufferLength, free, ArrayBufferAllocationType::Heap);
  591. }
  592. #else
  593. return HeapNew(ArrayBufferDetachedState<FreeFn>, buffer, bufferLength, free, ArrayBufferAllocationType::Heap);
  594. #endif
  595. }
  596. bool JavascriptArrayBuffer::IsValidAsmJsBufferLengthAlgo(uint length, bool forceCheck)
  597. {
  598. /*
  599. 1. length >= 2^16
  600. 2. length is power of 2 or (length > 2^24 and length is multiple of 2^24)
  601. 3. length is a multiple of 4K
  602. */
  603. const bool isLongEnough = length >= 0x10000;
  604. const bool isPow2 = ::Math::IsPow2(length);
  605. // No need to check for length > 2^24, because it already has to be non zero length
  606. const bool isMultipleOf2e24 = (length & 0xFFFFFF) == 0;
  607. const bool isPageSizeMultiple = (length % AutoSystemInfo::PageSize) == 0;
  608. return (
  609. #ifndef ENABLE_FAST_ARRAYBUFFER
  610. forceCheck &&
  611. #endif
  612. isLongEnough &&
  613. (isPow2 || isMultipleOf2e24) &&
  614. isPageSizeMultiple
  615. );
  616. }
  617. bool JavascriptArrayBuffer::IsValidAsmJsBufferLength(uint length, bool forceCheck)
  618. {
  619. return IsValidAsmJsBufferLengthAlgo(length, forceCheck);
  620. }
  621. bool JavascriptArrayBuffer::IsValidVirtualBufferLength(uint length)
  622. {
  623. #if ENABLE_FAST_ARRAYBUFFER
  624. return !PHASE_OFF1(Js::TypedArrayVirtualPhase) && IsValidAsmJsBufferLengthAlgo(length, true);
  625. #else
  626. return false;
  627. #endif
  628. }
  629. void JavascriptArrayBuffer::Finalize(bool isShutdown)
  630. {
  631. // In debugger scenario, ScriptAuthor can create scriptContext and delete scriptContext
  632. // explicitly. So for the builtin, while javascriptLibrary is still alive fine, the
  633. // matching scriptContext might have been deleted and the javascriptLibrary->scriptContext
  634. // field reset (but javascriptLibrary is still alive).
  635. // Use the recycler field off library instead of scriptcontext to avoid av.
  636. // Recycler may not be available at Dispose. We need to
  637. // free the memory and report that it has been freed at the same
  638. // time. Otherwise, AllocationPolicyManager is unable to provide correct feedback
  639. #if ENABLE_FAST_ARRAYBUFFER
  640. //AsmJS Virtual Free
  641. if (buffer && IsValidVirtualBufferLength(this->bufferLength))
  642. {
  643. FreeMemAlloc(buffer);
  644. }
  645. else
  646. {
  647. free(buffer);
  648. }
  649. #else
  650. free(buffer);
  651. #endif
  652. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  653. recycler->ReportExternalMemoryFree(bufferLength);
  654. buffer = nullptr;
  655. bufferLength = 0;
  656. }
  657. void JavascriptArrayBuffer::Dispose(bool isShutdown)
  658. {
  659. /* See JavascriptArrayBuffer::Finalize */
  660. }
  661. // Copy memory from src to dst, truncate if dst smaller, zero extra memory
  662. // if dst larger
  663. static void MemCpyZero(__bcount(dstSize) BYTE* dst, size_t dstSize,
  664. __in_bcount(count) const BYTE* src, size_t count)
  665. {
  666. js_memcpy_s(dst, dstSize, src, min(dstSize, count));
  667. if (dstSize > count)
  668. {
  669. ZeroMemory(dst + count, dstSize - count);
  670. }
  671. }
  672. // Same as realloc but zero newly allocated portion if newSize > oldSize
  673. static BYTE* ReallocZero(BYTE* ptr, size_t oldSize, size_t newSize)
  674. {
  675. BYTE* ptrNew = (BYTE*)realloc(ptr, newSize);
  676. if (ptrNew && newSize > oldSize)
  677. {
  678. ZeroMemory(ptrNew + oldSize, newSize - oldSize);
  679. }
  680. return ptrNew;
  681. }
  682. ArrayBuffer * JavascriptArrayBuffer::TransferInternal(uint32 newBufferLength)
  683. {
  684. ArrayBuffer* newArrayBuffer;
  685. Recycler* recycler = this->GetRecycler();
  686. if (this->bufferLength > 0)
  687. {
  688. ReportDifferentialAllocation(newBufferLength);
  689. }
  690. if (newBufferLength == 0 || this->bufferLength == 0)
  691. {
  692. newArrayBuffer = GetLibrary()->CreateArrayBuffer(newBufferLength);
  693. if (newBufferLength > 0 && !newArrayBuffer->GetByteLength())
  694. {
  695. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  696. }
  697. }
  698. else
  699. {
  700. BYTE * newBuffer = nullptr;
  701. if (IsValidVirtualBufferLength(this->bufferLength))
  702. {
  703. if (IsValidVirtualBufferLength(newBufferLength))
  704. {
  705. // we are transferring between an optimized buffer using a length that can be optimized
  706. if (newBufferLength < this->bufferLength)
  707. {
  708. #pragma prefast(suppress:6250, "Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs).")
  709. VirtualFree(this->buffer + newBufferLength, this->bufferLength - newBufferLength, MEM_DECOMMIT);
  710. }
  711. else if (newBufferLength > this->bufferLength)
  712. {
  713. LPVOID newMem = VirtualAlloc(this->buffer + this->bufferLength, newBufferLength - this->bufferLength, MEM_COMMIT, PAGE_READWRITE);
  714. if (!newMem)
  715. {
  716. recycler->ReportExternalMemoryFailure(newBufferLength - this->bufferLength);
  717. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  718. }
  719. }
  720. newBuffer = this->buffer;
  721. }
  722. else
  723. {
  724. // we are transferring from an optimized buffer, but the new length isn't compatible, so start over and copy to new memory
  725. newBuffer = (BYTE*)malloc(newBufferLength);
  726. if (!newBuffer)
  727. {
  728. recycler->ReportExternalMemoryFailure(newBufferLength - this->bufferLength);
  729. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  730. }
  731. MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
  732. }
  733. }
  734. else
  735. {
  736. if (IsValidVirtualBufferLength(newBufferLength))
  737. {
  738. // we are transferring from an unoptimized buffer, but new length can be optimized, so move to that
  739. newBuffer = (BYTE*)AsmJsVirtualAllocator(newBufferLength);
  740. if (!newBuffer)
  741. {
  742. recycler->ReportExternalMemoryFailure(newBufferLength - this->bufferLength);
  743. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  744. }
  745. MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
  746. }
  747. else if (newBufferLength != this->bufferLength)
  748. {
  749. // both sides will just be regular ArrayBuffer, so realloc
  750. newBuffer = ReallocZero(this->buffer, this->bufferLength, newBufferLength);
  751. if (!newBuffer)
  752. {
  753. recycler->ReportExternalMemoryFailure(newBufferLength - this->bufferLength);
  754. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  755. }
  756. }
  757. else
  758. {
  759. newBuffer = this->buffer;
  760. }
  761. }
  762. newArrayBuffer = GetLibrary()->CreateArrayBuffer(newBuffer, newBufferLength);
  763. }
  764. AutoDiscardPTR<Js::ArrayBufferDetachedStateBase> state(DetachAndGetState());
  765. state->MarkAsClaimed();
  766. return newArrayBuffer;
  767. }
  768. void JavascriptArrayBuffer::ReportDifferentialAllocation(uint32 newBufferLength)
  769. {
  770. Recycler* recycler = this->GetRecycler();
  771. // Report differential external memory allocation.
  772. // If current bufferLength == 0, new ArrayBuffer creation records the allocation
  773. // so no need to do it here.
  774. if (newBufferLength != this->bufferLength)
  775. {
  776. // Expanding buffer
  777. if (newBufferLength > this->bufferLength)
  778. {
  779. if (!recycler->ReportExternalMemoryAllocation(newBufferLength - this->bufferLength))
  780. {
  781. recycler->CollectNow<CollectOnTypedArrayAllocation>();
  782. if (!recycler->ReportExternalMemoryAllocation(newBufferLength - this->bufferLength))
  783. {
  784. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  785. }
  786. }
  787. }
  788. // Contracting buffer
  789. else
  790. {
  791. recycler->ReportExternalMemoryFree(this->bufferLength - newBufferLength);
  792. }
  793. }
  794. }
  795. #if ENABLE_TTD
  796. TTD::NSSnapObjects::SnapObjectType JavascriptArrayBuffer::GetSnapTag_TTD() const
  797. {
  798. return TTD::NSSnapObjects::SnapObjectType::SnapArrayBufferObject;
  799. }
  800. void JavascriptArrayBuffer::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  801. {
  802. TTD::NSSnapObjects::SnapArrayBufferInfo* sabi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapArrayBufferInfo>();
  803. sabi->Length = this->GetByteLength();
  804. if (sabi->Length == 0)
  805. {
  806. sabi->Buff = nullptr;
  807. }
  808. else
  809. {
  810. sabi->Buff = alloc.SlabAllocateArray<byte>(sabi->Length);
  811. memcpy(sabi->Buff, this->GetBuffer(), sabi->Length);
  812. }
  813. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapArrayBufferInfo*, TTD::NSSnapObjects::SnapObjectType::SnapArrayBufferObject>(objData, sabi);
  814. }
  815. #endif
  816. WebAssemblyArrayBuffer::WebAssemblyArrayBuffer(uint32 length, DynamicType * type) :
  817. #ifndef ENABLE_FAST_ARRAYBUFFER
  818. // Treat as a normal JavascriptArrayBuffer
  819. JavascriptArrayBuffer(length, type) {}
  820. #else
  821. JavascriptArrayBuffer(length, type, WasmVirtualAllocator)
  822. {
  823. // Make sure we always have a buffer even if the length is 0
  824. if (buffer == nullptr)
  825. {
  826. // We want to allocate an empty buffer using virtual memory
  827. Assert(length == 0);
  828. buffer = (BYTE*)WasmVirtualAllocator(0);
  829. if (buffer == nullptr)
  830. {
  831. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  832. }
  833. }
  834. }
  835. #endif
  836. WebAssemblyArrayBuffer::WebAssemblyArrayBuffer(byte* buffer, uint32 length, DynamicType * type):
  837. JavascriptArrayBuffer(buffer, length, type)
  838. {
  839. }
  840. WebAssemblyArrayBuffer* WebAssemblyArrayBuffer::Create(byte* buffer, uint32 length, DynamicType * type)
  841. {
  842. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  843. WebAssemblyArrayBuffer* result;
  844. if (buffer)
  845. {
  846. result = RecyclerNewFinalized(recycler, WebAssemblyArrayBuffer, buffer, length, type);
  847. }
  848. else
  849. {
  850. result = RecyclerNewFinalized(recycler, WebAssemblyArrayBuffer, length, type);
  851. }
  852. Assert(result);
  853. recycler->AddExternalMemoryUsage(length);
  854. return result;
  855. }
  856. bool WebAssemblyArrayBuffer::IsValidVirtualBufferLength(uint length)
  857. {
  858. #if ENABLE_FAST_ARRAYBUFFER
  859. return true;
  860. #else
  861. return false;
  862. #endif
  863. }
  864. ArrayBuffer * WebAssemblyArrayBuffer::TransferInternal(uint32 newBufferLength)
  865. {
  866. #if ENABLE_FAST_ARRAYBUFFER
  867. ReportDifferentialAllocation(newBufferLength);
  868. Assert(this->buffer);
  869. AssertMsg(newBufferLength > this->bufferLength, "The only supported scenario in WebAssembly is to grow the memory");
  870. if (newBufferLength > this->bufferLength)
  871. {
  872. LPVOID newMem = VirtualAlloc(this->buffer + this->bufferLength, newBufferLength - this->bufferLength, MEM_COMMIT, PAGE_READWRITE);
  873. if (!newMem)
  874. {
  875. Recycler* recycler = this->GetRecycler();
  876. recycler->ReportExternalMemoryFailure(newBufferLength);
  877. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  878. }
  879. }
  880. ArrayBuffer* newArrayBuffer = GetLibrary()->CreateWebAssemblyArrayBuffer(this->buffer, newBufferLength);
  881. AutoDiscardPTR<Js::ArrayBufferDetachedStateBase> state(DetachAndGetState());
  882. state->MarkAsClaimed();
  883. return newArrayBuffer;
  884. #else
  885. return JavascriptArrayBuffer::TransferInternal(newBufferLength);
  886. #endif
  887. }
  888. ProjectionArrayBuffer::ProjectionArrayBuffer(uint32 length, DynamicType * type) :
  889. ArrayBuffer(length, type, CoTaskMemAlloc)
  890. {
  891. }
  892. ProjectionArrayBuffer::ProjectionArrayBuffer(byte* buffer, uint32 length, DynamicType * type) :
  893. ArrayBuffer(buffer, length, type)
  894. {
  895. }
  896. ProjectionArrayBuffer* ProjectionArrayBuffer::Create(uint32 length, DynamicType * type)
  897. {
  898. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  899. recycler->AddExternalMemoryUsage(length);
  900. return RecyclerNewFinalized(recycler, ProjectionArrayBuffer, length, type);
  901. }
  902. ProjectionArrayBuffer* ProjectionArrayBuffer::Create(byte* buffer, uint32 length, DynamicType * type)
  903. {
  904. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  905. // This is user passed [in] buffer, user should AddExternalMemoryUsage before calling jscript, but
  906. // I don't see we ask everyone to do this. Let's add the memory pressure here as well.
  907. recycler->AddExternalMemoryUsage(length);
  908. return RecyclerNewFinalized(recycler, ProjectionArrayBuffer, buffer, length, type);
  909. }
  910. void ProjectionArrayBuffer::Dispose(bool isShutdown)
  911. {
  912. CoTaskMemFree(buffer);
  913. }
  914. ArrayBuffer * ProjectionArrayBuffer::TransferInternal(uint32 newBufferLength)
  915. {
  916. ArrayBuffer* newArrayBuffer;
  917. if (newBufferLength == 0 || this->bufferLength == 0)
  918. {
  919. newArrayBuffer = GetLibrary()->CreateProjectionArraybuffer(newBufferLength);
  920. }
  921. else
  922. {
  923. BYTE * newBuffer = (BYTE*)CoTaskMemRealloc(this->buffer, newBufferLength);
  924. if (!newBuffer)
  925. {
  926. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  927. }
  928. newArrayBuffer = GetLibrary()->CreateProjectionArraybuffer(newBuffer, newBufferLength);
  929. }
  930. AutoDiscardPTR<Js::ArrayBufferDetachedStateBase> state(DetachAndGetState());
  931. state->MarkAsClaimed();
  932. return newArrayBuffer;
  933. }
  934. ExternalArrayBuffer::ExternalArrayBuffer(byte *buffer, uint32 length, DynamicType *type)
  935. : ArrayBuffer(buffer, length, type)
  936. {
  937. }
  938. #if ENABLE_TTD
  939. TTD::NSSnapObjects::SnapObjectType ExternalArrayBuffer::GetSnapTag_TTD() const
  940. {
  941. //We re-map ExternalArrayBuffers to regular buffers since the 'real' host will be gone when we replay
  942. return TTD::NSSnapObjects::SnapObjectType::SnapArrayBufferObject;
  943. }
  944. void ExternalArrayBuffer::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  945. {
  946. TTD::NSSnapObjects::SnapArrayBufferInfo* sabi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapArrayBufferInfo>();
  947. sabi->Length = this->GetByteLength();
  948. if(sabi->Length == 0)
  949. {
  950. sabi->Buff = nullptr;
  951. }
  952. else
  953. {
  954. sabi->Buff = alloc.SlabAllocateArray<byte>(sabi->Length);
  955. memcpy(sabi->Buff, this->GetBuffer(), sabi->Length);
  956. }
  957. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapArrayBufferInfo*, TTD::NSSnapObjects::SnapObjectType::SnapArrayBufferObject>(objData, sabi);
  958. }
  959. #endif
  960. }