1/*
2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "JSArrayBufferView.h"
28
29#include "GenericTypedArrayViewInlines.h"
30#include "JSArrayBuffer.h"
31#include "JSCInlines.h"
32#include "JSGenericTypedArrayViewInlines.h"
33#include "JSTypedArrays.h"
34#include "TypeError.h"
35#include "TypedArrayController.h"
36#include "TypedArrays.h"
37#include <wtf/Gigacage.h>
38
39namespace JSC {
40
41const ClassInfo JSArrayBufferView::s_info = {
42 "ArrayBufferView", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferView)
43};
44
45String JSArrayBufferView::toStringName(const JSObject*, JSGlobalObject*)
46{
47 return "Object"_s;
48}
49
50JSArrayBufferView::ConstructionContext::ConstructionContext(
51 Structure* structure, uint32_t length, void* vector)
52 : m_structure(structure)
53 , m_vector(vector, length)
54 , m_length(length)
55 , m_mode(FastTypedArray)
56 , m_butterfly(nullptr)
57{
58 ASSERT(vector == removeArrayPtrTag(vector));
59 RELEASE_ASSERT(length <= fastSizeLimit);
60}
61
62JSArrayBufferView::ConstructionContext::ConstructionContext(
63 VM& vm, Structure* structure, uint32_t length, uint32_t elementSize,
64 InitializationMode mode)
65 : m_structure(0)
66 , m_length(length)
67 , m_butterfly(0)
68{
69 if (length <= fastSizeLimit) {
70 // Attempt GC allocation.
71 void* temp;
72 size_t size = sizeOf(length, elementSize);
73 temp = vm.primitiveGigacageAuxiliarySpace.allocateNonVirtual(vm, size, nullptr, AllocationFailureMode::ReturnNull);
74 if (!temp)
75 return;
76
77 m_structure = structure;
78 m_vector = VectorType(temp, length);
79 m_mode = FastTypedArray;
80
81 if (mode == ZeroFill) {
82 uint64_t* asWords = static_cast<uint64_t*>(vector());
83 for (unsigned i = size / sizeof(uint64_t); i--;)
84 asWords[i] = 0;
85 }
86
87 return;
88 }
89
90 // Don't allow a typed array to use more than 2GB.
91 if (length > static_cast<unsigned>(INT_MAX) / elementSize)
92 return;
93
94 size_t size = static_cast<size_t>(length) * static_cast<size_t>(elementSize);
95 m_vector = VectorType(Gigacage::tryMalloc(Gigacage::Primitive, size), length);
96 if (!m_vector)
97 return;
98 if (mode == ZeroFill)
99 memset(vector(), 0, size);
100
101 vm.heap.reportExtraMemoryAllocated(static_cast<size_t>(length) * elementSize);
102
103 m_structure = structure;
104 m_mode = OversizeTypedArray;
105}
106
107JSArrayBufferView::ConstructionContext::ConstructionContext(
108 VM& vm, Structure* structure, RefPtr<ArrayBuffer>&& arrayBuffer,
109 unsigned byteOffset, unsigned length)
110 : m_structure(structure)
111 , m_length(length)
112 , m_mode(WastefulTypedArray)
113{
114 ASSERT(arrayBuffer->data() == removeArrayPtrTag(arrayBuffer->data()));
115 m_vector = VectorType(static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset, length);
116 IndexingHeader indexingHeader;
117 indexingHeader.setArrayBuffer(arrayBuffer.get());
118 m_butterfly = Butterfly::create(vm, 0, 0, 0, true, indexingHeader, 0);
119}
120
121JSArrayBufferView::ConstructionContext::ConstructionContext(
122 Structure* structure, RefPtr<ArrayBuffer>&& arrayBuffer,
123 unsigned byteOffset, unsigned length, DataViewTag)
124 : m_structure(structure)
125 , m_length(length)
126 , m_mode(DataViewMode)
127 , m_butterfly(0)
128{
129 ASSERT(arrayBuffer->data() == removeArrayPtrTag(arrayBuffer->data()));
130 m_vector = VectorType(static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset, length);
131}
132
133JSArrayBufferView::JSArrayBufferView(VM& vm, ConstructionContext& context)
134 : Base(vm, context.structure(), nullptr)
135 , m_length(context.length())
136 , m_mode(context.mode())
137{
138 setButterfly(vm, context.butterfly());
139 ASSERT(context.vector() == removeArrayPtrTag(context.vector()));
140 m_vector.setWithoutBarrier(context.vector(), m_length);
141}
142
143void JSArrayBufferView::finishCreation(VM& vm)
144{
145 Base::finishCreation(vm);
146 ASSERT(jsDynamicCast<JSArrayBufferView*>(vm, this));
147 switch (m_mode) {
148 case FastTypedArray:
149 return;
150 case OversizeTypedArray:
151 vm.heap.addFinalizer(this, finalize);
152 return;
153 case WastefulTypedArray:
154 vm.heap.addReference(this, butterfly()->indexingHeader()->arrayBuffer());
155 return;
156 case DataViewMode:
157 ASSERT(!butterfly());
158 vm.heap.addReference(this, jsCast<JSDataView*>(this)->possiblySharedBuffer());
159 return;
160 }
161 RELEASE_ASSERT_NOT_REACHED();
162}
163
164void JSArrayBufferView::visitChildren(JSCell* cell, SlotVisitor& visitor)
165{
166 JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
167 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
168 Base::visitChildren(cell, visitor);
169
170 if (thisObject->hasArrayBuffer()) {
171 WTF::loadLoadFence();
172 ArrayBuffer* buffer = thisObject->possiblySharedBuffer();
173 RELEASE_ASSERT(buffer);
174 visitor.addOpaqueRoot(buffer);
175 }
176}
177
178bool JSArrayBufferView::put(
179 JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value,
180 PutPropertySlot& slot)
181{
182 JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
183
184 if (UNLIKELY(isThisValueAltered(slot, thisObject)))
185 return ordinarySetSlow(globalObject, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
186
187 return Base::put(thisObject, globalObject, propertyName, value, slot);
188}
189
190ArrayBuffer* JSArrayBufferView::unsharedBuffer()
191{
192 ArrayBuffer* result = possiblySharedBuffer();
193 RELEASE_ASSERT(!result->isShared());
194 return result;
195}
196
197void JSArrayBufferView::finalize(JSCell* cell)
198{
199 JSArrayBufferView* thisObject = static_cast<JSArrayBufferView*>(cell);
200 ASSERT(thisObject->m_mode == OversizeTypedArray || thisObject->m_mode == WastefulTypedArray);
201 if (thisObject->m_mode == OversizeTypedArray)
202 Gigacage::free(Gigacage::Primitive, thisObject->vector());
203}
204
205JSArrayBuffer* JSArrayBufferView::unsharedJSBuffer(JSGlobalObject* globalObject)
206{
207 VM& vm = globalObject->vm();
208 return vm.m_typedArrayController->toJS(globalObject, this->globalObject(vm), unsharedBuffer());
209}
210
211JSArrayBuffer* JSArrayBufferView::possiblySharedJSBuffer(JSGlobalObject* globalObject)
212{
213 VM& vm = globalObject->vm();
214 return vm.m_typedArrayController->toJS(globalObject, this->globalObject(vm), possiblySharedBuffer());
215}
216
217void JSArrayBufferView::neuter()
218{
219 auto locker = holdLock(cellLock());
220 RELEASE_ASSERT(hasArrayBuffer());
221 RELEASE_ASSERT(!isShared());
222 m_length = 0;
223 m_vector.clear();
224}
225
226static const constexpr size_t ElementSizeData[] = {
227#define FACTORY(type) sizeof(typename type ## Adaptor::Type),
228 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
229#undef FACTORY
230};
231
232#define FACTORY(type) static_assert(std::is_final<JS ## type ## Array>::value, "");
233FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
234#undef FACTORY
235
236static inline size_t elementSize(JSType type)
237{
238 ASSERT(type >= Int8ArrayType && type <= Float64ArrayType);
239 return ElementSizeData[type - Int8ArrayType];
240}
241
242ArrayBuffer* JSArrayBufferView::slowDownAndWasteMemory()
243{
244 ASSERT(m_mode == FastTypedArray || m_mode == OversizeTypedArray);
245
246 // We play this game because we want this to be callable even from places that
247 // don't have access to CallFrame* or the VM, and we only allocate so little
248 // memory here that it's not necessary to trigger a GC - just accounting what
249 // we have done is good enough. The sort of bizarre exception to the "allocating
250 // little memory" is when we transfer a backing buffer into the C heap; this
251 // will temporarily get counted towards heap footprint (incorrectly, in the case
252 // of adopting an oversize typed array) but we don't GC here anyway. That's
253 // almost certainly fine. The worst case is if you created a ton of fast typed
254 // arrays, and did nothing but caused all of them to slow down and waste memory.
255 // In that case, your memory footprint will double before the GC realizes what's
256 // up. But if you do *anything* to trigger a GC watermark check, it will know
257 // that you *had* done those allocations and it will GC appropriately.
258 Heap* heap = Heap::heap(this);
259 VM& vm = heap->vm();
260 DeferGCForAWhile deferGC(*heap);
261
262 RELEASE_ASSERT(!hasIndexingHeader(vm));
263 Structure* structure = this->structure(vm);
264 setButterfly(vm, Butterfly::createOrGrowArrayRight(
265 butterfly(), vm, this, structure,
266 structure->outOfLineCapacity(), false, 0, 0));
267
268 RefPtr<ArrayBuffer> buffer;
269 unsigned byteLength = m_length * elementSize(type());
270
271 switch (m_mode) {
272 case FastTypedArray:
273 buffer = ArrayBuffer::create(vector(), byteLength);
274 break;
275
276 case OversizeTypedArray:
277 // FIXME: consider doing something like "subtracting" from extra memory
278 // cost, since right now this case will cause the GC to think that we reallocated
279 // the whole buffer.
280 buffer = ArrayBuffer::createAdopted(vector(), byteLength);
281 break;
282
283 default:
284 RELEASE_ASSERT_NOT_REACHED();
285 break;
286 }
287
288 {
289 auto locker = holdLock(cellLock());
290 butterfly()->indexingHeader()->setArrayBuffer(buffer.get());
291 m_vector.setWithoutBarrier(buffer->data(), m_length);
292 WTF::storeStoreFence();
293 m_mode = WastefulTypedArray;
294 }
295 heap->addReference(this, buffer.get());
296
297 return buffer.get();
298}
299
300// Allocates the full-on native buffer and moves data into the C heap if
301// necessary. Note that this never allocates in the GC heap.
302RefPtr<ArrayBufferView> JSArrayBufferView::possiblySharedImpl()
303{
304 ArrayBuffer* buffer = possiblySharedBuffer();
305 unsigned byteOffset = this->byteOffset();
306 unsigned length = this->length();
307 switch (type()) {
308#define FACTORY(type) \
309 case type ## ArrayType: \
310 return type ## Array::tryCreate(buffer, byteOffset, length);
311 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
312#undef FACTORY
313 case DataViewType:
314 return DataView::create(buffer, byteOffset, length);
315 default:
316 RELEASE_ASSERT_NOT_REACHED();
317 return nullptr;
318 }
319}
320
321} // namespace JSC
322
323namespace WTF {
324
325using namespace JSC;
326
327void printInternal(PrintStream& out, TypedArrayMode mode)
328{
329 switch (mode) {
330 case FastTypedArray:
331 out.print("FastTypedArray");
332 return;
333 case OversizeTypedArray:
334 out.print("OversizeTypedArray");
335 return;
336 case WastefulTypedArray:
337 out.print("WastefulTypedArray");
338 return;
339 case DataViewMode:
340 out.print("DataViewMode");
341 return;
342 }
343 RELEASE_ASSERT_NOT_REACHED();
344}
345
346} // namespace WTF
347
348