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