0a1edf2629e961b69a193a7ab40e6575b68c396d
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSGenericTypedArrayViewInlines.h
1 /*
2  * Copyright (C) 2013 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 #ifndef JSGenericTypedArrayViewInlines_h
27 #define JSGenericTypedArrayViewInlines_h
28
29 #include "ArrayBufferView.h"
30 #include "DeferGC.h"
31 #include "Error.h"
32 #include "ExceptionHelpers.h"
33 #include "JSGenericTypedArrayView.h"
34 #include "Reject.h"
35 #include "TypedArrays.h"
36
37 namespace JSC {
38
39 template<typename Adaptor>
40 JSGenericTypedArrayView<Adaptor>::JSGenericTypedArrayView(
41     VM& vm, ConstructionContext& context)
42     : Base(vm, context)
43 {
44 }
45
46 template<typename Adaptor>
47 JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
48     ExecState* exec, Structure* structure, unsigned length)
49 {
50     ConstructionContext context(exec->vm(), structure, length, sizeof(typename Adaptor::Type));
51     if (!context) {
52         throwError(exec, createOutOfMemoryError(structure->globalObject()));
53         return 0;
54     }
55     JSGenericTypedArrayView* result =
56         new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap))
57         JSGenericTypedArrayView(exec->vm(), context);
58     result->finishCreation(exec->vm());
59     return result;
60 }
61
62 template<typename Adaptor>
63 JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::createUninitialized(
64     ExecState* exec, Structure* structure, unsigned length)
65 {
66     ConstructionContext context(
67         exec->vm(), structure, length, sizeof(typename Adaptor::Type),
68         ConstructionContext::DontInitialize);
69     if (!context) {
70         throwError(exec, createOutOfMemoryError(structure->globalObject()));
71         return 0;
72     }
73     JSGenericTypedArrayView* result =
74         new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap))
75         JSGenericTypedArrayView(exec->vm(), context);
76     result->finishCreation(exec->vm());
77     return result;
78 }
79
80 template<typename Adaptor>
81 JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
82     ExecState* exec, Structure* structure, PassRefPtr<ArrayBuffer> passedBuffer,
83     unsigned byteOffset, unsigned length)
84 {
85     RefPtr<ArrayBuffer> buffer = passedBuffer;
86     if (!ArrayBufferView::verifySubRange<typename Adaptor::Type>(buffer, byteOffset, length)) {
87         throwError(
88             exec, createRangeError(exec, "Byte offset and length out of range of buffer"));
89         return 0;
90     }
91     ConstructionContext context(exec->vm(), structure, buffer, byteOffset, length);
92     ASSERT(context);
93     JSGenericTypedArrayView* result =
94         new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap))
95         JSGenericTypedArrayView(exec->vm(), context);
96     result->finishCreation(exec->vm());
97     return result;
98 }
99
100 template<typename Adaptor>
101 JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
102     VM& vm, Structure* structure, PassRefPtr<typename Adaptor::ViewType> impl)
103 {
104     RefPtr<ArrayBuffer> buffer = impl->buffer();
105     ConstructionContext context(vm, structure, buffer, impl->byteOffset(), impl->length());
106     ASSERT(context);
107     JSGenericTypedArrayView* result =
108         new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap))
109         JSGenericTypedArrayView(vm, context);
110     result->finishCreation(vm);
111     return result;
112 }
113
114 template<typename Adaptor>
115 JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
116     Structure* structure, JSGlobalObject* globalObject,
117     PassRefPtr<typename Adaptor::ViewType> impl)
118 {
119     return create(globalObject->vm(), structure, impl);
120 }
121
122 template<typename Adaptor>
123 bool JSGenericTypedArrayView<Adaptor>::validateRange(
124     ExecState* exec, unsigned offset, unsigned length)
125 {
126     if (canAccessRangeQuickly(offset, length))
127         return true;
128     
129     throwError(exec, createRangeError(exec, "Range consisting of offset and length are out of bounds"));
130     return false;
131 }
132
133 template<typename Adaptor>
134 template<typename OtherAdaptor>
135 bool JSGenericTypedArrayView<Adaptor>::setWithSpecificType(
136     ExecState* exec, JSGenericTypedArrayView<OtherAdaptor>* other,
137     unsigned offset, unsigned length)
138 {
139     // Handle the hilarious case: the act of getting the length could have resulted
140     // in neutering. Well, no. That'll never happen because there cannot be
141     // side-effects on getting the length of a typed array. But predicting where there
142     // are, or aren't, side-effects is a fool's game so we resort to this cheap
143     // check. Worst case, if we're wrong, people start seeing less things get copied
144     // but we won't have a security vulnerability.
145     length = std::min(length, other->length());
146     
147     if (!validateRange(exec, offset, length))
148         return false;
149     
150     if (other->length() != length) {
151         throwError(exec, createRangeError(exec, "Length of incoming array changed unexpectedly."));
152         return false;
153     }
154     
155     // This method doesn't support copying between the same array. Note that
156     // set() will only call this if the types differ, which implicitly guarantees
157     // that we can't be the same array. This is relevant because the way we detect
158     // non-overlapping is by checking if either (a) either array doesn't have a
159     // backing buffer or (b) the backing buffers are different, but that doesn't
160     // catch the case where it's the *same* array - fortunately though, this code
161     // path never needs to worry about that case.
162     ASSERT(static_cast<JSCell*>(this) != static_cast<JSCell*>(other));
163     
164     // 1) If the two arrays are non-overlapping, we can copy in any order we like
165     //    and we don't need an intermediate buffer. Arrays are definitely
166     //    non-overlapping if either one of them has no backing buffer (that means
167     //    that it *owns* its philosophical backing buffer) or if they have
168     //    different backing buffers.
169     // 2) If the two arrays overlap but have the same element size, we can do a
170     //    memmove-like copy where we flip-flop direction based on which vector
171     //    starts before the other:
172     //    A) If the destination vector is before the source vector, then a forward
173     //       copy is in order.
174     //    B) If the destination vector is after the source vector, then a backward
175     //       copy is in order.
176     // 3) If we have different element sizes and there is a chance of overlap then
177     //    we need an intermediate vector.
178     
179     // NB. Comparisons involving elementSize will be constant-folded by template
180     // specialization.
181
182     unsigned otherElementSize = sizeof(typename OtherAdaptor::Type);
183     
184     // Handle cases (1) and (2B).
185     if (!hasArrayBuffer() || !other->hasArrayBuffer()
186         || existingBuffer() != other->existingBuffer()
187         || (elementSize == otherElementSize && vector() > other->vector())) {
188         for (unsigned i = length; i--;) {
189             setIndexQuicklyToNativeValue(
190                 offset + i, OtherAdaptor::template convertTo<Adaptor>(
191                     other->getIndexQuicklyAsNativeValue(i)));
192         }
193         return true;
194     }
195     
196     // Now we either have (2A) or (3) - so first we try to cover (2A).
197     if (elementSize == otherElementSize) {
198         for (unsigned i = 0; i < length; ++i) {
199             setIndexQuicklyToNativeValue(
200                 offset + i, OtherAdaptor::template convertTo<Adaptor>(
201                     other->getIndexQuicklyAsNativeValue(i)));
202         }
203         return true;
204     }
205     
206     // Fail: we need an intermediate transfer buffer (i.e. case (3)).
207     Vector<typename Adaptor::Type, 32> transferBuffer(length);
208     for (unsigned i = length; i--;) {
209         transferBuffer[i] = OtherAdaptor::template convertTo<Adaptor>(
210             other->getIndexQuicklyAsNativeValue(i));
211     }
212     for (unsigned i = length; i--;)
213         setIndexQuicklyToNativeValue(offset + i, transferBuffer[i]);
214     
215     return true;
216 }
217
218 template<typename Adaptor>
219 bool JSGenericTypedArrayView<Adaptor>::set(
220     ExecState* exec, JSObject* object, unsigned offset, unsigned length)
221 {
222     const ClassInfo* ci = object->classInfo();
223     if (ci->typedArrayStorageType == Adaptor::typeValue) {
224         // The super fast case: we can just memcpy since we're the same type.
225         JSGenericTypedArrayView* other = jsCast<JSGenericTypedArrayView*>(object);
226         length = std::min(length, other->length());
227         
228         if (!validateRange(exec, offset, length))
229             return false;
230         
231         memmove(typedVector() + offset, other->typedVector(), other->byteLength());
232         return true;
233     }
234     
235     switch (ci->typedArrayStorageType) {
236     case TypeInt8:
237         return setWithSpecificType<Int8Adaptor>(
238             exec, jsCast<JSInt8Array*>(object), offset, length);
239     case TypeInt16:
240         return setWithSpecificType<Int16Adaptor>(
241             exec, jsCast<JSInt16Array*>(object), offset, length);
242     case TypeInt32:
243         return setWithSpecificType<Int32Adaptor>(
244             exec, jsCast<JSInt32Array*>(object), offset, length);
245     case TypeUint8:
246         return setWithSpecificType<Uint8Adaptor>(
247             exec, jsCast<JSUint8Array*>(object), offset, length);
248     case TypeUint8Clamped:
249         return setWithSpecificType<Uint8ClampedAdaptor>(
250             exec, jsCast<JSUint8ClampedArray*>(object), offset, length);
251     case TypeUint16:
252         return setWithSpecificType<Uint16Adaptor>(
253             exec, jsCast<JSUint16Array*>(object), offset, length);
254     case TypeUint32:
255         return setWithSpecificType<Uint32Adaptor>(
256             exec, jsCast<JSUint32Array*>(object), offset, length);
257     case TypeFloat32:
258         return setWithSpecificType<Float32Adaptor>(
259             exec, jsCast<JSFloat32Array*>(object), offset, length);
260     case TypeFloat64:
261         return setWithSpecificType<Float64Adaptor>(
262             exec, jsCast<JSFloat64Array*>(object), offset, length);
263     case NotTypedArray:
264     case TypeDataView: {
265         if (!validateRange(exec, offset, length))
266             return false;
267         // We could optimize this case. But right now, we don't.
268         for (unsigned i = 0; i < length; ++i) {
269             if (!setIndexQuickly(exec, offset + i, object->get(exec, i)))
270                 return false;
271         }
272         return true;
273     } }
274     
275     RELEASE_ASSERT_NOT_REACHED();
276     return false;
277 }
278
279 template<typename Adaptor>
280 ArrayBuffer* JSGenericTypedArrayView<Adaptor>::existingBuffer()
281 {
282     return existingBufferInButterfly();
283 }
284
285 template<typename Adaptor>
286 bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot(
287     JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
288 {
289     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
290     if (propertyName == exec->propertyNames().length) {
291         slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->length()));
292         return true;
293     }
294     
295     if (propertyName == exec->propertyNames().byteLength) {
296         slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->byteLength()));
297         return true;
298     }
299     
300     unsigned index = propertyName.asIndex();
301     if (index != PropertyName::NotAnIndex && thisObject->canGetIndexQuickly(index)) {
302         slot.setValue(thisObject, DontDelete | ReadOnly, thisObject->getIndexQuickly(index));
303         return true;
304     }
305     
306     return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
307 }
308
309 template<typename Adaptor>
310 void JSGenericTypedArrayView<Adaptor>::put(
311     JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value,
312     PutPropertySlot& slot)
313 {
314     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
315     
316     if (propertyName == exec->propertyNames().length) {
317         // Firefox appears to simply ignore attempts to store to the length property.
318         // Even in strict mode. I will do the same.
319         return;
320     }
321     
322     unsigned index = propertyName.asIndex();
323     if (index != PropertyName::NotAnIndex) {
324         putByIndex(thisObject, exec, index, value, slot.isStrictMode());
325         return;
326     }
327     
328     Base::put(thisObject, exec, propertyName, value, slot);
329 }
330
331 template<typename Adaptor>
332 bool JSGenericTypedArrayView<Adaptor>::defineOwnProperty(
333     JSObject* object, ExecState* exec, PropertyName propertyName,
334     const PropertyDescriptor& descriptor, bool shouldThrow)
335 {
336     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
337     
338     // This is matching Firefox behavior. In particular, it rejects all attempts to
339     // defineOwnProperty for indexed properties on typed arrays, even if they're out
340     // of bounds.
341     if (propertyName == exec->propertyNames().length
342         || propertyName.asIndex() != PropertyName::NotAnIndex)
343         return reject(exec, shouldThrow, "Attempting to write to a read-only typed array property.");
344     
345     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
346 }
347
348 template<typename Adaptor>
349 bool JSGenericTypedArrayView<Adaptor>::deleteProperty(
350     JSCell* cell, ExecState* exec, PropertyName propertyName)
351 {
352     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
353     
354     if (propertyName == exec->propertyNames().length
355         || propertyName.asIndex() != PropertyName::NotAnIndex)
356         return false;
357     
358     return Base::deleteProperty(thisObject, exec, propertyName);
359 }
360
361 template<typename Adaptor>
362 bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex(
363     JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot)
364 {
365     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
366     
367     if (propertyName > MAX_ARRAY_INDEX) {
368         return thisObject->methodTable()->getOwnPropertySlot(
369             thisObject, exec, Identifier::from(exec, propertyName), slot);
370     }
371     
372     if (!thisObject->canGetIndexQuickly(propertyName))
373         return false;
374     
375     slot.setValue(thisObject, None, thisObject->getIndexQuickly(propertyName));
376     return true;
377 }
378
379 template<typename Adaptor>
380 void JSGenericTypedArrayView<Adaptor>::putByIndex(
381     JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
382 {
383     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
384     
385     if (propertyName > MAX_ARRAY_INDEX) {
386         PutPropertySlot slot(shouldThrow);
387         thisObject->methodTable()->put(
388             thisObject, exec, Identifier::from(exec, propertyName), value, slot);
389         return;
390     }
391     
392     if (!thisObject->canSetIndexQuickly(propertyName)) {
393         // Yes, really. Firefox returns without throwing anything if you store beyond
394         // the bounds.
395         return;
396     }
397     
398     thisObject->setIndexQuickly(exec, propertyName, value);
399 }
400
401 template<typename Adaptor>
402 bool JSGenericTypedArrayView<Adaptor>::deletePropertyByIndex(
403     JSCell* cell, ExecState* exec, unsigned propertyName)
404 {
405     if (propertyName > MAX_ARRAY_INDEX) {
406         return cell->methodTable()->deleteProperty(
407             cell, exec, Identifier::from(exec, propertyName));
408     }
409     
410     return false;
411 }
412
413 template<typename Adaptor>
414 void JSGenericTypedArrayView<Adaptor>::getOwnNonIndexPropertyNames(
415     JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
416 {
417     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
418     
419     if (mode == IncludeDontEnumProperties)
420         array.add(exec->propertyNames().length);
421     
422     Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode);
423 }
424
425 template<typename Adaptor>
426 void JSGenericTypedArrayView<Adaptor>::getOwnPropertyNames(
427     JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
428 {
429     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
430     
431     for (unsigned i = 0; i < thisObject->m_length; ++i)
432         array.add(Identifier(exec, String::number(i)));
433     
434     return Base::getOwnPropertyNames(object, exec, array, mode);
435 }
436
437 template<typename Adaptor>
438 void JSGenericTypedArrayView<Adaptor>::visitChildren(JSCell* cell, SlotVisitor& visitor)
439 {
440     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
441     
442     switch (thisObject->m_mode) {
443     case FastTypedArray: {
444         visitor.copyLater(thisObject, TypedArrayVectorCopyToken, thisObject->m_vector, thisObject->byteSize());
445         break;
446     }
447         
448     case OversizeTypedArray: {
449         visitor.reportExtraMemoryUsage(thisObject->byteSize());
450         break;
451     }
452         
453     case WastefulTypedArray:
454         break;
455         
456     case DataViewMode:
457         RELEASE_ASSERT_NOT_REACHED();
458         break;
459     }
460     
461     Base::visitChildren(thisObject, visitor);
462 }
463
464 template<typename Adaptor>
465 void JSGenericTypedArrayView<Adaptor>::copyBackingStore(
466     JSCell* cell, CopyVisitor& visitor, CopyToken token)
467 {
468     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
469     
470     if (token == TypedArrayVectorCopyToken
471         && visitor.checkIfShouldCopy(thisObject->m_vector)) {
472         void* oldVector = thisObject->m_vector;
473         void* newVector = visitor.allocateNewSpace(thisObject->byteSize());
474         memcpy(newVector, oldVector, thisObject->byteSize());
475         thisObject->m_vector = newVector;
476         visitor.didCopy(oldVector, thisObject->byteSize());
477     }
478     
479     Base::copyBackingStore(thisObject, visitor, token);
480 }
481
482 template<typename Adaptor>
483 ArrayBuffer* JSGenericTypedArrayView<Adaptor>::slowDownAndWasteMemory(JSArrayBufferView* object)
484 {
485     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
486     
487     // We play this game because we want this to be callable even from places that
488     // don't have access to ExecState* or the VM, and we only allocate so little
489     // memory here that it's not necessary to trigger a GC - just accounting what
490     // we have done is good enough. The sort of bizarro exception to the "allocating
491     // little memory" is when we transfer a backing buffer into the C heap; this
492     // will temporarily get counted towards heap footprint (incorrectly, in the case
493     // of adopting an oversize typed array) but we don't GC here anyway. That's
494     // almost certainly fine. The worst case is if you created a ton of fast typed
495     // arrays, and did nothing but caused all of them to slow down and waste memory.
496     // In that case, your memory footprint will double before the GC realizes what's
497     // up. But if you do *anything* to trigger a GC watermark check, it will know
498     // that you *had* done those allocations and it will GC appropriately.
499     Heap* heap = Heap::heap(thisObject);
500     DeferGCForAWhile deferGC(*heap);
501     
502     ASSERT(!thisObject->hasIndexingHeader());
503
504     size_t size = thisObject->byteSize();
505     
506     if (thisObject->m_mode == FastTypedArray
507         && !thisObject->m_butterfly && size >= sizeof(IndexingHeader)) {
508         // Reuse already allocated memory if at all possible.
509         thisObject->m_butterfly =
510             static_cast<IndexingHeader*>(thisObject->m_vector)->butterfly();
511     } else {
512         thisObject->m_butterfly = Butterfly::createOrGrowArrayRight(
513             thisObject->m_butterfly, *heap->vm(), thisObject, thisObject->structure(),
514             thisObject->structure()->outOfLineCapacity(), false, 0, 0);
515     }
516
517     RefPtr<ArrayBuffer> buffer;
518     
519     switch (thisObject->m_mode) {
520     case FastTypedArray:
521         buffer = ArrayBuffer::create(thisObject->m_vector, thisObject->byteLength());
522         break;
523         
524     case OversizeTypedArray:
525         // FIXME: consider doing something like "subtracting" from extra memory
526         // cost, since right now this case will cause the GC to think that we reallocated
527         // the whole buffer.
528         buffer = ArrayBuffer::createAdopted(thisObject->m_vector, thisObject->byteLength());
529         break;
530         
531     default:
532         RELEASE_ASSERT_NOT_REACHED();
533         break;
534     }
535
536     thisObject->butterfly()->indexingHeader()->setArrayBuffer(buffer.get());
537     thisObject->m_vector = buffer->data();
538     thisObject->m_mode = WastefulTypedArray;
539     heap->addReference(thisObject, buffer.get());
540     
541     return buffer.get();
542 }
543
544 template<typename Adaptor>
545 PassRefPtr<ArrayBufferView>
546 JSGenericTypedArrayView<Adaptor>::getTypedArrayImpl(JSArrayBufferView* object)
547 {
548     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
549     return thisObject->typedImpl();
550 }
551
552 } // namespace JSC
553
554 #endif // JSGenericTypedArrayViewInlines_h
555