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 JSGenericTypedArrayView_h
27#define JSGenericTypedArrayView_h
28
29#include "JSArrayBufferView.h"
30#include "ToNativeFromValue.h"
31
32namespace JSC {
33
34JS_EXPORT_PRIVATE const ClassInfo* getInt8ArrayClassInfo();
35JS_EXPORT_PRIVATE const ClassInfo* getInt16ArrayClassInfo();
36JS_EXPORT_PRIVATE const ClassInfo* getInt32ArrayClassInfo();
37JS_EXPORT_PRIVATE const ClassInfo* getUint8ArrayClassInfo();
38JS_EXPORT_PRIVATE const ClassInfo* getUint8ClampedArrayClassInfo();
39JS_EXPORT_PRIVATE const ClassInfo* getUint16ArrayClassInfo();
40JS_EXPORT_PRIVATE const ClassInfo* getUint32ArrayClassInfo();
41JS_EXPORT_PRIVATE const ClassInfo* getFloat32ArrayClassInfo();
42JS_EXPORT_PRIVATE const ClassInfo* getFloat64ArrayClassInfo();
43
44// A typed array view is our representation of a typed array object as seen
45// from JavaScript. For example:
46//
47// var o = new Int8Array(100);
48//
49// Here, 'o' points to a JSGenericTypedArrayView<int8_t>.
50//
51// Views contain five fields:
52//
53//     Structure* S     // from JSCell
54//     Butterfly* B     // from JSObject
55//     ElementType* V
56//     uint32_t L
57//     TypedArrayMode M
58//
59// These fields take up a total of four pointer-width words. FIXME: Make
60// it take less words!
61//
62// B is usually unused but may stored some additional "overflow" data for
63// one of the modes. V always points to the base of the typed array's data,
64// and may point to either GC-managed copied space, or data in the C heap;
65// which of those things it points to is governed by the mode although for
66// simple accesses to the view you can just read from the pointer either
67// way. M specifies the mode of the view. L is the length, in units that
68// depend on the view's type.
69
70// The JSGenericTypedArrayView is templatized by an Adaptor that controls
71// the element type and how it's converted; it should obey the following
72// interface; I use int8_t as an example:
73//
74// struct Adaptor {
75//     typedef int8_t Type;
76//     typedef Int8Array ViewType;
77//     typedef JSInt8Array JSViewType;
78//     static int8_t toNativeFromInt32(int32_t);
79//     static int8_t toNativeFromUint32(uint32_t);
80//     static int8_t toNativeFromDouble(double);
81//     static JSValue toJSValue(int8_t);
82//     static double toDouble(int8_t);
83//     template<T> static T::Type convertTo(uint8_t);
84// };
85
86template<typename Adaptor>
87class JSGenericTypedArrayView : public JSArrayBufferView {
88public:
89    typedef JSArrayBufferView Base;
90    static const unsigned elementSize = sizeof(typename Adaptor::Type);
91
92protected:
93    JSGenericTypedArrayView(VM&, ConstructionContext&);
94
95public:
96    static JSGenericTypedArrayView* create(ExecState*, Structure*, unsigned length);
97    static JSGenericTypedArrayView* createUninitialized(ExecState*, Structure*, unsigned length);
98    static JSGenericTypedArrayView* create(ExecState*, Structure*, PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length);
99    static JSGenericTypedArrayView* create(VM&, Structure*, PassRefPtr<typename Adaptor::ViewType> impl);
100    static JSGenericTypedArrayView* create(Structure*, JSGlobalObject*, PassRefPtr<typename Adaptor::ViewType> impl);
101
102    unsigned byteLength() const { return m_length * sizeof(typename Adaptor::Type); }
103    size_t byteSize() const { return sizeOf(m_length, sizeof(typename Adaptor::Type)); }
104
105    const typename Adaptor::Type* typedVector() const
106    {
107        return static_cast<const typename Adaptor::Type*>(m_vector);
108    }
109    typename Adaptor::Type* typedVector()
110    {
111        return static_cast<typename Adaptor::Type*>(m_vector);
112    }
113
114    // These methods are meant to match indexed access methods that JSObject
115    // supports - hence the slight redundancy.
116    bool canGetIndexQuickly(unsigned i)
117    {
118        return i < m_length;
119    }
120    bool canSetIndexQuickly(unsigned i)
121    {
122        return i < m_length;
123    }
124
125    typename Adaptor::Type getIndexQuicklyAsNativeValue(unsigned i)
126    {
127        ASSERT(i < m_length);
128        return typedVector()[i];
129    }
130
131    double getIndexQuicklyAsDouble(unsigned i)
132    {
133        return Adaptor::toDouble(getIndexQuicklyAsNativeValue(i));
134    }
135
136    JSValue getIndexQuickly(unsigned i)
137    {
138        return Adaptor::toJSValue(getIndexQuicklyAsNativeValue(i));
139    }
140
141    void setIndexQuicklyToNativeValue(unsigned i, typename Adaptor::Type value)
142    {
143        ASSERT(i < m_length);
144        typedVector()[i] = value;
145    }
146
147    void setIndexQuicklyToDouble(unsigned i, double value)
148    {
149        setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value));
150    }
151
152    void setIndexQuickly(unsigned i, JSValue value)
153    {
154        setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value));
155    }
156
157    bool setIndex(ExecState* exec, unsigned i, JSValue jsValue)
158    {
159        typename Adaptor::Type value = toNativeFromValue<Adaptor>(exec, jsValue);
160        if (exec->hadException())
161            return false;
162
163        if (i >= m_length)
164            return false;
165
166        setIndexQuicklyToNativeValue(i, value);
167        return true;
168    }
169
170    bool canAccessRangeQuickly(unsigned offset, unsigned length)
171    {
172        return offset <= m_length
173            && offset + length <= m_length
174            // check overflow
175            && offset + length >= offset;
176    }
177
178    // Like canSetQuickly, except: if it returns false, it will throw the
179    // appropriate exception.
180    bool validateRange(ExecState*, unsigned offset, unsigned length);
181
182    // Returns true if successful, and false on error; if it returns false
183    // then it will have thrown an exception.
184    bool set(ExecState*, JSObject*, unsigned offset, unsigned length);
185
186    PassRefPtr<typename Adaptor::ViewType> typedImpl()
187    {
188        return Adaptor::ViewType::create(buffer(), byteOffset(), length());
189    }
190
191    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
192    {
193        return Structure::create(vm, globalObject, prototype, TypeInfo(typeForTypedArrayType(Adaptor::typeValue), StructureFlags), info(), NonArray);
194    }
195
196    static const ClassInfo s_info; // This is never accessed directly, since that would break linkage on some compilers.
197
198    static const ClassInfo* info()
199    {
200        switch (Adaptor::typeValue) {
201        case TypeInt8:
202            return getInt8ArrayClassInfo();
203        case TypeInt16:
204            return getInt16ArrayClassInfo();
205        case TypeInt32:
206            return getInt32ArrayClassInfo();
207        case TypeUint8:
208            return getUint8ArrayClassInfo();
209        case TypeUint8Clamped:
210            return getUint8ClampedArrayClassInfo();
211        case TypeUint16:
212            return getUint16ArrayClassInfo();
213        case TypeUint32:
214            return getUint32ArrayClassInfo();
215        case TypeFloat32:
216            return getFloat32ArrayClassInfo();
217        case TypeFloat64:
218            return getFloat64ArrayClassInfo();
219        default:
220            RELEASE_ASSERT_NOT_REACHED();
221            return 0;
222        }
223    }
224
225    ArrayBuffer* existingBuffer();
226
227    static const TypedArrayType TypedArrayStorageType = Adaptor::typeValue;
228
229protected:
230    friend struct TypedArrayClassInfos;
231
232    static const unsigned StructureFlags = OverridesGetPropertyNames | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | Base::StructureFlags;
233
234    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
235    static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
236    static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
237    static bool deleteProperty(JSCell*, ExecState*, PropertyName);
238
239    static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
240    static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
241    static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
242
243    static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
244    static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
245
246    static void visitChildren(JSCell*, SlotVisitor&);
247    static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken);
248
249    // Allocates the full-on native buffer and moves data into the C heap if
250    // necessary. Note that this never allocates in the GC heap.
251    static ArrayBuffer* slowDownAndWasteMemory(JSArrayBufferView*);
252    static PassRefPtr<ArrayBufferView> getTypedArrayImpl(JSArrayBufferView*);
253
254private:
255    // Returns true if successful, and false on error; it will throw on error.
256    template<typename OtherAdaptor>
257    bool setWithSpecificType(
258        ExecState*, JSGenericTypedArrayView<OtherAdaptor>*,
259        unsigned offset, unsigned length);
260};
261
262template<typename Adaptor>
263inline PassRefPtr<typename Adaptor::ViewType> toNativeTypedView(JSValue value)
264{
265    typename Adaptor::JSViewType* wrapper = jsDynamicCast<typename Adaptor::JSViewType*>(value);
266    if (!wrapper)
267        return 0;
268    return wrapper->typedImpl();
269}
270
271} // namespace JSC
272
273#endif // JSGenericTypedArrayView_h
274
275