1/*
2 * Copyright (C) 2013-2018 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)
54 , m_length(length)
55 , m_mode(FastTypedArray)
56 , m_butterfly(nullptr)
57{
58 RELEASE_ASSERT(length <= fastSizeLimit);
59}
60
61JSArrayBufferView::ConstructionContext::ConstructionContext(
62 VM& vm, Structure* structure, uint32_t length, uint32_t elementSize,
63 InitializationMode mode)
64 : m_structure(0)
65 , m_length(length)
66 , m_butterfly(0)
67{
68 if (length <= fastSizeLimit) {
69 // Attempt GC allocation.
70 void* temp;
71 size_t size = sizeOf(length, elementSize);
72 temp = vm.primitiveGigacageAuxiliarySpace.allocateNonVirtual(vm, size, nullptr, AllocationFailureMode::ReturnNull);
73 if (!temp)
74 return;
75
76 m_structure = structure;
77 m_vector = temp;
78 m_mode = FastTypedArray;
79
80 if (mode == ZeroFill) {
81 uint64_t* asWords = static_cast<uint64_t*>(m_vector.getMayBeNull());
82 for (unsigned i = size / sizeof(uint64_t); i--;)
83 asWords[i] = 0;
84 }
85
86 return;
87 }
88
89 // Don't allow a typed array to use more than 2GB.
90 if (length > static_cast<unsigned>(INT_MAX) / elementSize)
91 return;
92
93 size_t size = static_cast<size_t>(length) * static_cast<size_t>(elementSize);
94 m_vector = Gigacage::tryMalloc(Gigacage::Primitive, size);
95 if (!m_vector)
96 return;
97 if (mode == ZeroFill)
98 memset(m_vector.get(), 0, size);
99
100 vm.heap.reportExtraMemoryAllocated(static_cast<size_t>(length) * elementSize);
101
102 m_structure = structure;
103 m_mode = OversizeTypedArray;
104}
105
106JSArrayBufferView::ConstructionContext::ConstructionContext(
107 VM& vm, Structure* structure, RefPtr<ArrayBuffer>&& arrayBuffer,
108 unsigned byteOffset, unsigned length)
109 : m_structure(structure)
110 , m_length(length)
111 , m_mode(WastefulTypedArray)
112{
113 m_vector = static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset;
114 IndexingHeader indexingHeader;
115 indexingHeader.setArrayBuffer(arrayBuffer.get());
116 m_butterfly = Butterfly::create(vm, 0, 0, 0, true, indexingHeader, 0);
117}
118
119JSArrayBufferView::ConstructionContext::ConstructionContext(
120 Structure* structure, RefPtr<ArrayBuffer>&& arrayBuffer,
121 unsigned byteOffset, unsigned length, DataViewTag)
122 : m_structure(structure)
123 , m_length(length)
124 , m_mode(DataViewMode)
125 , m_butterfly(0)
126{
127 m_vector = static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset;
128}
129
130JSArrayBufferView::JSArrayBufferView(VM& vm, ConstructionContext& context)
131 : Base(vm, context.structure(), nullptr)
132 , m_length(context.length())
133 , m_mode(context.mode())
134{
135 setButterfly(vm, context.butterfly());
136 m_vector.setWithoutBarrier(context.vector());
137}
138
139void JSArrayBufferView::finishCreation(VM& vm)
140{
141 Base::finishCreation(vm);
142 ASSERT(jsDynamicCast<JSArrayBufferView*>(vm, this));
143 switch (m_mode) {
144 case FastTypedArray:
145 return;
146 case OversizeTypedArray:
147 vm.heap.addFinalizer(this, finalize);
148 return;
149 case WastefulTypedArray:
150 vm.heap.addReference(this, butterfly()->indexingHeader()->arrayBuffer());
151 return;
152 case DataViewMode:
153 ASSERT(!butterfly());
154 vm.heap.addReference(this, jsCast<JSDataView*>(this)->possiblySharedBuffer());
155 return;
156 }
157 RELEASE_ASSERT_NOT_REACHED();
158}
159
160void JSArrayBufferView::visitChildren(JSCell* cell, SlotVisitor& visitor)
161{
162 JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
163 Base::visitChildren(cell, visitor);
164
165 if (thisObject->hasArrayBuffer()) {
166 WTF::loadLoadFence();
167 ArrayBuffer* buffer = thisObject->possiblySharedBuffer();
168 RELEASE_ASSERT(buffer);
169 visitor.addOpaqueRoot(buffer);
170 }
171}
172
173bool JSArrayBufferView::put(
174 JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value,
175 PutPropertySlot& slot)
176{
177 JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
178
179 if (UNLIKELY(isThisValueAltered(slot, thisObject)))
180 return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
181
182 return Base::put(thisObject, exec, propertyName, value, slot);
183}
184
185ArrayBuffer* JSArrayBufferView::unsharedBuffer()
186{
187 ArrayBuffer* result = possiblySharedBuffer();
188 RELEASE_ASSERT(!result->isShared());
189 return result;
190}
191
192void JSArrayBufferView::finalize(JSCell* cell)
193{
194 JSArrayBufferView* thisObject = static_cast<JSArrayBufferView*>(cell);
195 ASSERT(thisObject->m_mode == OversizeTypedArray || thisObject->m_mode == WastefulTypedArray);
196 if (thisObject->m_mode == OversizeTypedArray)
197 Gigacage::free(Gigacage::Primitive, thisObject->m_vector.get());
198}
199
200JSArrayBuffer* JSArrayBufferView::unsharedJSBuffer(ExecState* exec)
201{
202 VM& vm = exec->vm();
203 return vm.m_typedArrayController->toJS(exec, globalObject(vm), unsharedBuffer());
204}
205
206JSArrayBuffer* JSArrayBufferView::possiblySharedJSBuffer(ExecState* exec)
207{
208 VM& vm = exec->vm();
209 return vm.m_typedArrayController->toJS(exec, globalObject(vm), possiblySharedBuffer());
210}
211
212void JSArrayBufferView::neuter()
213{
214 auto locker = holdLock(cellLock());
215 RELEASE_ASSERT(hasArrayBuffer());
216 RELEASE_ASSERT(!isShared());
217 m_length = 0;
218 m_vector.clear();
219}
220
221static const constexpr size_t ElementSizeData[] = {
222#define FACTORY(type) sizeof(typename type ## Adaptor::Type),
223 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
224#undef FACTORY
225};
226
227#define FACTORY(type) static_assert(std::is_final<JS ## type ## Array>::value, "");
228FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
229#undef FACTORY
230
231static inline size_t elementSize(JSType type)
232{
233 ASSERT(type >= Int8ArrayType && type <= Float64ArrayType);
234 return ElementSizeData[type - Int8ArrayType];
235}
236
237ArrayBuffer* JSArrayBufferView::slowDownAndWasteMemory()
238{
239 ASSERT(m_mode == FastTypedArray || m_mode == OversizeTypedArray);
240
241 // We play this game because we want this to be callable even from places that
242 // don't have access to ExecState* or the VM, and we only allocate so little
243 // memory here that it's not necessary to trigger a GC - just accounting what
244 // we have done is good enough. The sort of bizarre exception to the "allocating
245 // little memory" is when we transfer a backing buffer into the C heap; this
246 // will temporarily get counted towards heap footprint (incorrectly, in the case
247 // of adopting an oversize typed array) but we don't GC here anyway. That's
248 // almost certainly fine. The worst case is if you created a ton of fast typed
249 // arrays, and did nothing but caused all of them to slow down and waste memory.
250 // In that case, your memory footprint will double before the GC realizes what's
251 // up. But if you do *anything* to trigger a GC watermark check, it will know
252 // that you *had* done those allocations and it will GC appropriately.
253 Heap* heap = Heap::heap(this);
254 VM& vm = *heap->vm();
255 DeferGCForAWhile deferGC(*heap);
256
257 RELEASE_ASSERT(!hasIndexingHeader(vm));
258 Structure* structure = this->structure(vm);
259 setButterfly(vm, Butterfly::createOrGrowArrayRight(
260 butterfly(), vm, this, structure,
261 structure->outOfLineCapacity(), false, 0, 0));
262
263 RefPtr<ArrayBuffer> buffer;
264 unsigned byteLength = m_length * elementSize(type());
265
266 switch (m_mode) {
267 case FastTypedArray:
268 buffer = ArrayBuffer::create(vector(), byteLength);
269 break;
270
271 case OversizeTypedArray:
272 // FIXME: consider doing something like "subtracting" from extra memory
273 // cost, since right now this case will cause the GC to think that we reallocated
274 // the whole buffer.
275 buffer = ArrayBuffer::createAdopted(vector(), byteLength);
276 break;
277
278 default:
279 RELEASE_ASSERT_NOT_REACHED();
280 break;
281 }
282
283 {
284 auto locker = holdLock(cellLock());
285 butterfly()->indexingHeader()->setArrayBuffer(buffer.get());
286 m_vector.setWithoutBarrier(buffer->data());
287 WTF::storeStoreFence();
288 m_mode = WastefulTypedArray;
289 }
290 heap->addReference(this, buffer.get());
291
292 return buffer.get();
293}
294
295// Allocates the full-on native buffer and moves data into the C heap if
296// necessary. Note that this never allocates in the GC heap.
297RefPtr<ArrayBufferView> JSArrayBufferView::possiblySharedImpl()
298{
299 ArrayBuffer* buffer = possiblySharedBuffer();
300 unsigned byteOffset = this->byteOffset();
301 unsigned length = this->length();
302 switch (type()) {
303#define FACTORY(type) \
304 case type ## ArrayType: \
305 return type ## Array::tryCreate(buffer, byteOffset, length);
306 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
307#undef FACTORY
308 case DataViewType:
309 return DataView::create(buffer, byteOffset, length);
310 default:
311 RELEASE_ASSERT_NOT_REACHED();
312 return nullptr;
313 }
314}
315
316} // namespace JSC
317
318namespace WTF {
319
320using namespace JSC;
321
322void printInternal(PrintStream& out, TypedArrayMode mode)
323{
324 switch (mode) {
325 case FastTypedArray:
326 out.print("FastTypedArray");
327 return;
328 case OversizeTypedArray:
329 out.print("OversizeTypedArray");
330 return;
331 case WastefulTypedArray:
332 out.print("WastefulTypedArray");
333 return;
334 case DataViewMode:
335 out.print("DataViewMode");
336 return;
337 }
338 RELEASE_ASSERT_NOT_REACHED();
339}
340
341} // namespace WTF
342
343