Update ANGLE
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSArrayBufferView.cpp
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
39 namespace JSC {
40
41 const ClassInfo JSArrayBufferView::s_info = {
42     "ArrayBufferView", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferView)
43 };
44
45 String JSArrayBufferView::toStringName(const JSObject*, JSGlobalObject*)
46 {
47     return "Object"_s;
48 }
49
50 JSArrayBufferView::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
62 JSArrayBufferView::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
107 JSArrayBufferView::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
121 JSArrayBufferView::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
133 JSArrayBufferView::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
143 void 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
164 void 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
178 bool 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
190 ArrayBuffer* JSArrayBufferView::unsharedBuffer()
191 {
192     ArrayBuffer* result = possiblySharedBuffer();
193     RELEASE_ASSERT(!result->isShared());
194     return result;
195 }
196     
197 void 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
205 JSArrayBuffer* JSArrayBufferView::unsharedJSBuffer(JSGlobalObject* globalObject)
206 {
207     VM& vm = globalObject->vm();
208     return vm.m_typedArrayController->toJS(globalObject, this->globalObject(vm), unsharedBuffer());
209 }
210
211 JSArrayBuffer* JSArrayBufferView::possiblySharedJSBuffer(JSGlobalObject* globalObject)
212 {
213     VM& vm = globalObject->vm();
214     return vm.m_typedArrayController->toJS(globalObject, this->globalObject(vm), possiblySharedBuffer());
215 }
216
217 void 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
226 static 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, "");
233 FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
234 #undef FACTORY
235
236 static inline size_t elementSize(JSType type)
237 {
238     ASSERT(type >= Int8ArrayType && type <= Float64ArrayType);
239     return ElementSizeData[type - Int8ArrayType];
240 }
241
242 ArrayBuffer* 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.
302 RefPtr<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
323 namespace WTF {
324
325 using namespace JSC;
326
327 void 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