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