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*, ExecState*)
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 Base::visitChildren(cell, visitor);
168
169 if (thisObject->hasArrayBuffer()) {
170 WTF::loadLoadFence();
171 ArrayBuffer* buffer = thisObject->possiblySharedBuffer();
172 RELEASE_ASSERT(buffer);
173 visitor.addOpaqueRoot(buffer);
174 }
175}
176
177bool JSArrayBufferView::put(
178 JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value,
179 PutPropertySlot& slot)
180{
181 JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
182
183 if (UNLIKELY(isThisValueAltered(slot, thisObject)))
184 return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
185
186 return Base::put(thisObject, exec, propertyName, value, slot);
187}
188
189ArrayBuffer* JSArrayBufferView::unsharedBuffer()
190{
191 ArrayBuffer* result = possiblySharedBuffer();
192 RELEASE_ASSERT(!result->isShared());
193 return result;
194}
195
196void JSArrayBufferView::finalize(JSCell* cell)
197{
198 JSArrayBufferView* thisObject = static_cast<JSArrayBufferView*>(cell);
199 ASSERT(thisObject->m_mode == OversizeTypedArray || thisObject->m_mode == WastefulTypedArray);
200 if (thisObject->m_mode == OversizeTypedArray)
201 Gigacage::free(Gigacage::Primitive, thisObject->vector());
202}
203
204JSArrayBuffer* JSArrayBufferView::unsharedJSBuffer(ExecState* exec)
205{
206 VM& vm = exec->vm();
207 return vm.m_typedArrayController->toJS(exec, globalObject(vm), unsharedBuffer());
208}
209
210JSArrayBuffer* JSArrayBufferView::possiblySharedJSBuffer(ExecState* exec)
211{
212 VM& vm = exec->vm();
213 return vm.m_typedArrayController->toJS(exec, globalObject(vm), possiblySharedBuffer());
214}
215
216void JSArrayBufferView::neuter()
217{
218 auto locker = holdLock(cellLock());
219 RELEASE_ASSERT(hasArrayBuffer());
220 RELEASE_ASSERT(!isShared());
221 m_length = 0;
222 m_vector.clear();
223}
224
225static const constexpr size_t ElementSizeData[] = {
226#define FACTORY(type) sizeof(typename type ## Adaptor::Type),
227 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
228#undef FACTORY
229};
230
231#define FACTORY(type) static_assert(std::is_final<JS ## type ## Array>::value, "");
232FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
233#undef FACTORY
234
235static inline size_t elementSize(JSType type)
236{
237 ASSERT(type >= Int8ArrayType && type <= Float64ArrayType);
238 return ElementSizeData[type - Int8ArrayType];
239}
240
241ArrayBuffer* JSArrayBufferView::slowDownAndWasteMemory()
242{
243 ASSERT(m_mode == FastTypedArray || m_mode == OversizeTypedArray);
244
245 // We play this game because we want this to be callable even from places that
246 // don't have access to ExecState* or the VM, and we only allocate so little
247 // memory here that it's not necessary to trigger a GC - just accounting what
248 // we have done is good enough. The sort of bizarre exception to the "allocating
249 // little memory" is when we transfer a backing buffer into the C heap; this
250 // will temporarily get counted towards heap footprint (incorrectly, in the case
251 // of adopting an oversize typed array) but we don't GC here anyway. That's
252 // almost certainly fine. The worst case is if you created a ton of fast typed
253 // arrays, and did nothing but caused all of them to slow down and waste memory.
254 // In that case, your memory footprint will double before the GC realizes what's
255 // up. But if you do *anything* to trigger a GC watermark check, it will know
256 // that you *had* done those allocations and it will GC appropriately.
257 Heap* heap = Heap::heap(this);
258 VM& vm = *heap->vm();
259 DeferGCForAWhile deferGC(*heap);
260
261 RELEASE_ASSERT(!hasIndexingHeader(vm));
262 Structure* structure = this->structure(vm);
263 setButterfly(vm, Butterfly::createOrGrowArrayRight(
264 butterfly(), vm, this, structure,
265 structure->outOfLineCapacity(), false, 0, 0));
266
267 RefPtr<ArrayBuffer> buffer;
268 unsigned byteLength = m_length * elementSize(type());
269
270 switch (m_mode) {
271 case FastTypedArray:
272 buffer = ArrayBuffer::create(vector(), byteLength);
273 break;
274
275 case OversizeTypedArray:
276 // FIXME: consider doing something like "subtracting" from extra memory
277 // cost, since right now this case will cause the GC to think that we reallocated
278 // the whole buffer.
279 buffer = ArrayBuffer::createAdopted(vector(), byteLength);
280 break;
281
282 default:
283 RELEASE_ASSERT_NOT_REACHED();
284 break;
285 }
286
287 {
288 auto locker = holdLock(cellLock());
289 butterfly()->indexingHeader()->setArrayBuffer(buffer.get());
290 m_vector.setWithoutBarrier(buffer->data(), m_length);
291 WTF::storeStoreFence();
292 m_mode = WastefulTypedArray;
293 }
294 heap->addReference(this, buffer.get());
295
296 return buffer.get();
297}
298
299// Allocates the full-on native buffer and moves data into the C heap if
300// necessary. Note that this never allocates in the GC heap.
301RefPtr<ArrayBufferView> JSArrayBufferView::possiblySharedImpl()
302{
303 ArrayBuffer* buffer = possiblySharedBuffer();
304 unsigned byteOffset = this->byteOffset();
305 unsigned length = this->length();
306 switch (type()) {
307#define FACTORY(type) \
308 case type ## ArrayType: \
309 return type ## Array::tryCreate(buffer, byteOffset, length);
310 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
311#undef FACTORY
312 case DataViewType:
313 return DataView::create(buffer, byteOffset, length);
314 default:
315 RELEASE_ASSERT_NOT_REACHED();
316 return nullptr;
317 }
318}
319
320} // namespace JSC
321
322namespace WTF {
323
324using namespace JSC;
325
326void printInternal(PrintStream& out, TypedArrayMode mode)
327{
328 switch (mode) {
329 case FastTypedArray:
330 out.print("FastTypedArray");
331 return;
332 case OversizeTypedArray:
333 out.print("OversizeTypedArray");
334 return;
335 case WastefulTypedArray:
336 out.print("WastefulTypedArray");
337 return;
338 case DataViewMode:
339 out.print("DataViewMode");
340 return;
341 }
342 RELEASE_ASSERT_NOT_REACHED();
343}
344
345} // namespace WTF
346
347