File indexing completed on 2024-04-28 15:23:06

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2013 Bernd Buschinski <b.buschinski@googlemail.com>
0004  *
0005  *  This library is free software; you can redistribute it and/or
0006  *  modify it under the terms of the GNU Lesser General Public
0007  *  License as published by the Free Software Foundation; either
0008  *  version 2 of the License, or (at your option) any later version.
0009  *
0010  *  This library is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  *  Lesser General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU Lesser General Public
0016  *  License along with this library; if not, write to the Free Software
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 
0020 #ifndef KJS_ARRAYBUFFERVIEW_H
0021 #define KJS_ARRAYBUFFERVIEW_H
0022 
0023 #include "ecma/kjs_arraybuffer.h"
0024 
0025 #include <kjs/object.h>
0026 #include <kjs/function.h>
0027 #include <kjs/function_object.h>
0028 #include <kjs/operations.h>
0029 #include <kjs/array_instance.h>
0030 #include <dom/dom_exception.h>
0031 
0032 namespace KJS
0033 {
0034 class ArrayBuffer;
0035 
0036 // Keep enum outside of template classes, for lut tables
0037 namespace ArrayBufferViewBase
0038 {
0039 enum {
0040     Buffer, ByteLength, ByteOffset, Subarray, Length, Set, BytesPerElement
0041 };
0042 }
0043 
0044 //type, TypedArrayClass
0045 template <class T, class U>
0046 class ArrayBufferViewConstructorImp : public KJS::FunctionPrototype
0047 {
0048 public:
0049     ArrayBufferViewConstructorImp(ExecState *exec, DOM::DocumentImpl *d);
0050     bool implementsConstruct() const override;
0051     using KJS::JSObject::construct;
0052     JSObject *construct(ExecState *exec, const List &args) override;
0053 
0054     JSValue *getValueProperty(ExecState *exec, int token) const;
0055     using KJS::JSObject::getOwnPropertySlot;
0056     bool getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot) override;
0057 private:
0058     SharedPtr<DOM::DocumentImpl> doc;
0059 };
0060 
0061 //type, TypedArrayPrototype
0062 template <class T, class P>
0063 class ArrayBufferView : public JSObject
0064 {
0065 public:
0066     explicit ArrayBufferView(ExecState *exec, ArrayBuffer *buffer, size_t byteOffset, size_t byteLength);
0067     virtual ~ArrayBufferView();
0068 
0069     bool getOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot) override;
0070     bool getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot) override;
0071     JSValue *getValueProperty(ExecState *exec, int token) const;
0072 
0073     void put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr = None) override;
0074     void put(ExecState *exec, unsigned propertyName, JSValue *value, int attr = None) override;
0075 
0076     ArrayBuffer *buffer() const
0077     {
0078         return m_buffer;
0079     }
0080     size_t byteLength() const
0081     {
0082         return m_byteLength;
0083     }
0084     size_t byteOffset() const
0085     {
0086         return m_byteOffset;
0087     }
0088     size_t length() const
0089     {
0090         return m_length;
0091     }
0092     inline T *bufferStart() const
0093     {
0094         return m_bufferStart;
0095     }
0096 
0097 private:
0098     // checks if the pos is a valid array index, returns false if not
0099     inline bool checkIndex(ExecState *exec, unsigned pos);
0100 
0101     ProtectedPtr<ArrayBuffer> m_buffer;
0102     size_t m_byteOffset;
0103     size_t m_byteLength;
0104     size_t m_length;
0105     T *m_bufferStart;
0106 };
0107 
0108 //unrolled KJS_DEFINE_PROTOTYPE(ArrayBufferViewProto), for template sake
0109 
0110 //type, TypedArrayClass
0111 template <class T, class U>
0112 class ArrayBufferViewProto : public KJS::JSObject
0113 {
0114 public:
0115     bool getOwnPropertySlot(KJS::ExecState *, const KJS::Identifier &, KJS::PropertySlot &) override;
0116     using JSObject::getOwnPropertySlot;
0117 protected:
0118     ArrayBufferViewProto(KJS::ExecState *exec);
0119 };
0120 
0121 //unrolled KJS_IMPLEMENT_PROTOFUNC(ArrayBufferViewProtoFunc), for template sake
0122 template <class T, class U>
0123 class ArrayBufferViewProtoFunc : public KJS::InternalFunctionImp
0124 {
0125 public:
0126     ArrayBufferViewProtoFunc<T, U>(KJS::ExecState *exec, int i, int len, const KJS::Identifier &name)
0127         : InternalFunctionImp(static_cast<KJS::FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name), id(i)
0128     {
0129         put(exec, exec->propertyNames().length, KJS::jsNumber(len), KJS::DontDelete | KJS::ReadOnly | KJS::DontEnum);
0130     }
0131 
0132     KJS::JSValue *callAsFunction(KJS::ExecState *exec, KJS::JSObject *thisObj, const KJS::List &args) override;
0133 private:
0134     int id;
0135 };
0136 }
0137 
0138 #include "kjs_arraybufferview.lut.h"
0139 
0140 namespace KJS
0141 {
0142 
0143 // unrolled KJS_IMPLEMENT_PROTOTYPE("ArrayBufferView", ArrayBufferViewProto, ArrayBufferViewProtoFunc, ObjectPrototype)
0144 // for template sake
0145 template <class T, class U>
0146 bool ArrayBufferViewProto<T, U>::getOwnPropertySlot(KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::PropertySlot &slot)
0147 {
0148     return KJS::getStaticFunctionSlot<ArrayBufferViewProtoFunc<T, U>, KJS::JSObject>(exec, &ArrayBufferViewProtoTable, this, propertyName, slot);
0149 }
0150 
0151 template <class T, class U>
0152 ArrayBufferViewProto<T, U>::ArrayBufferViewProto(KJS::ExecState *exec)
0153     : KJS::JSObject(ObjectPrototype::self(exec))
0154 {
0155 }
0156 // unroll end
0157 
0158 // -------------------- ArrayBufferViewProtoFunc ---------------------
0159 
0160 template <class T, class U>
0161 JSValue *ArrayBufferViewProtoFunc<T, U>::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
0162 {
0163     if (!thisObj->inherits(&U::info)) {
0164         return jsUndefined();
0165     }
0166     U *view = static_cast<U *>(thisObj);
0167 
0168     switch (id) {
0169     case ArrayBufferViewBase::Subarray: {
0170         //Subarray takes max long/signed size_t
0171         // If start or end are negative, it refers to an index from the end of the array
0172         ssize_t begin = 0;
0173         ssize_t end = 0;
0174         double tmp;
0175         if (args[0]->getNumber(tmp)) {
0176             begin = static_cast<ssize_t>(tmp);
0177         }
0178         if (args.size() >= 2 && args[1]->getNumber(tmp)) {
0179             end = static_cast<ssize_t>(tmp);
0180         }
0181 
0182         // turn negative start/end into a valid positive index
0183         if (begin < 0 && view->length() > static_cast<size_t>(-begin)) {
0184             begin = view->length() + begin;
0185         }
0186         if (end < 0 && view->length() > static_cast<size_t>(-end)) {
0187             end = view->length() + end;
0188         }
0189 
0190         if (static_cast<size_t>(begin) > view->length()) {
0191             begin = view->length();
0192         }
0193         if (static_cast<size_t>(end) > view->length()) {
0194             end = 0;
0195         }
0196 
0197         size_t length = 0;
0198         if (begin < end) {
0199             length = (end - begin) * sizeof(T);
0200         }
0201 
0202         U *newView = new U(exec, view->buffer(), begin * sizeof(T), length);
0203         return newView;
0204     }
0205     case ArrayBufferViewBase::Set: {
0206         JSObject *obj = args[0]->getObject();
0207         if (!obj) {
0208             return jsUndefined();
0209         }
0210         if (obj->inherits(&U::info)) {
0211             U *other = static_cast<U *>(obj);
0212             double tmp;
0213             size_t offset = 0;
0214             if (args.size() >= 2 && args[1]->getNumber(tmp) && tmp > 0) {
0215                 offset = static_cast<size_t>(tmp) * sizeof(T);
0216             }
0217 
0218             if (offset > other->byteLength() || other->byteLength() - offset > view->byteLength()) {
0219                 setDOMException(exec, DOM::DOMException::INDEX_SIZE_ERR);
0220                 return jsUndefined();
0221             }
0222             memcpy(view->buffer()->buffer(), other->buffer()->buffer() + offset, std::max<ssize_t>(static_cast<ssize_t>(other->byteLength()) - static_cast<ssize_t>(offset), 0));
0223             return jsUndefined();
0224         } else if (obj->inherits(&ArrayInstance::info)) {
0225             ArrayInstance *array = static_cast<ArrayInstance *>(obj);
0226             if (array->getLength() > view->length()) {
0227                 setDOMException(exec, DOM::DOMException::INDEX_SIZE_ERR);
0228                 return jsUndefined();
0229             }
0230             for (unsigned i = 0; i < array->getLength(); ++i) {
0231                 view->put(exec, i, array->getItem(i));
0232             }
0233             return jsUndefined();
0234         } else {
0235             return jsUndefined();
0236         }
0237     }
0238     default:
0239         ASSERT_NOT_REACHED();
0240         return jsUndefined();
0241     }
0242 }
0243 
0244 // -------------------- ArrayBufferViewConstructorImp ---------------------
0245 
0246 template <class T, class U>
0247 ArrayBufferViewConstructorImp<T, U>::ArrayBufferViewConstructorImp(ExecState *exec, DOM::DocumentImpl *d)
0248     : KJS::FunctionPrototype(exec),
0249       doc(d)
0250 {
0251 }
0252 
0253 template <class T, class U>
0254 bool ArrayBufferViewConstructorImp<T, U>::implementsConstruct() const
0255 {
0256     return true;
0257 }
0258 
0259 template <class T, class U>
0260 JSObject *ArrayBufferViewConstructorImp<T, U>::construct(ExecState *exec, const List &args)
0261 {
0262     JSType type = args[0]->type();
0263 
0264     switch (type) {
0265     case ObjectType: {
0266         JSObject *obj = args[0]->getObject();
0267         if (!obj) {
0268             return throwError(exec, TypeError);
0269         }
0270         if (obj->inherits(&ArrayBuffer::info)) {
0271             // new ArrayBufferView(ArrayBuffer, [byteOffset[, byteLength]])
0272             ArrayBuffer *buf = static_cast<ArrayBuffer *>(obj);
0273 
0274             size_t byteOffset = 0, byteLength = 0;
0275             double tmp;
0276             if (args.size() >= 2 && args[1]->getNumber(tmp) && tmp > 0) {
0277                 byteOffset = static_cast<size_t>(tmp);
0278             }
0279 
0280             if (args.size() >= 3 && args[2]->getNumber(tmp) && tmp > 0) {
0281                 byteLength = static_cast<size_t>(tmp) * sizeof(T);
0282             }
0283 
0284             return new U(exec, buf, byteOffset, byteLength);
0285         } else if (obj->inherits(&ArrayInstance::info)) {
0286             // new ArrayBufferView(Array)
0287             ArrayInstance *arr = dynamic_cast<ArrayInstance *>(obj);
0288             ArrayBuffer *buf = new ArrayBuffer(arr->getLength()*sizeof(T));
0289             U *view = new U(exec, buf, 0, 0);
0290             for (unsigned i = 0; i < arr->getLength(); ++i) {
0291                 view->put(exec, i, arr->getItem(i));
0292             }
0293             return view;
0294         } else if (obj->inherits(&U::info)) {
0295             // new ArrayBufferView(ArrayBufferView)
0296             U *arr = static_cast<U *>(obj);
0297             ArrayBuffer *buf = new ArrayBuffer(arr->buffer()->buffer(), arr->byteLength());
0298             U *view = new U(exec, buf, 0, 0);
0299             return view;
0300         } else {
0301             break;
0302         }
0303     }
0304     case NumberType: {
0305         // new ArrayBufferView(Length)
0306         size_t length = 0;
0307         double lengthF = args[0]->getNumber();
0308         if (!KJS::isNaN(lengthF) && !KJS::isInf(lengthF) && lengthF > 0) {
0309             length = static_cast<size_t>(lengthF);
0310         }
0311         ArrayBuffer *buf = new ArrayBuffer(length * sizeof(T));
0312         return new U(exec, buf, 0, 0);
0313     }
0314     default:
0315         break;
0316     }
0317     // new ArrayBufferView(), create empty ArrayBuffer
0318     ArrayBuffer *buf = new ArrayBuffer(0);
0319     return new U(exec, buf, 0, 0);
0320 }
0321 
0322 template <class T, class U>
0323 JSValue *ArrayBufferViewConstructorImp<T, U>::getValueProperty(ExecState *, int) const
0324 {
0325     // switch (id)
0326     // {
0327     // case ArrayBufferViewFuncImp<T>::BytesPerElement:
0328     return jsNumber(sizeof(T));
0329     // }
0330 }
0331 
0332 template <class T, class U>
0333 bool ArrayBufferViewConstructorImp<T, U>::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0334 {
0335     return getStaticPropertySlot<ArrayBufferViewProtoFunc<T, U>, ArrayBufferViewConstructorImp<T, U>, KJS::FunctionPrototype>(exec, &ArrayBufferViewConstTable, this, propertyName, slot);
0336 }
0337 
0338 // -------------------- ArrayBufferView ---------------------
0339 
0340 template <class T, class P>
0341 ArrayBufferView<T, P>::ArrayBufferView(KJS::ExecState *exec, KJS::ArrayBuffer *buffer, size_t byteOffset, size_t byteLength)
0342     : JSObject(),
0343       m_buffer(buffer),
0344       m_byteOffset(byteOffset)
0345 {
0346     if (byteLength == 0) {
0347         if (byteOffset < buffer->byteLength()) {
0348             m_byteLength = buffer->byteLength() - byteOffset;
0349         } else {
0350             m_byteLength = 0;
0351         }
0352     } else {
0353         m_byteLength = byteLength;
0354     }
0355     m_length = m_byteLength / sizeof(T);
0356     setPrototype(P::self(exec));
0357     m_bufferStart = reinterpret_cast<T *>(m_buffer->buffer() + m_byteOffset);
0358 }
0359 
0360 template <class T, class P>
0361 ArrayBufferView<T, P>::~ArrayBufferView()
0362 {
0363 }
0364 
0365 template <class T, class P>
0366 bool ArrayBufferView<T, P>::getOwnPropertySlot(ExecState *exec, unsigned int i, PropertySlot &slot)
0367 {
0368     if (!checkIndex(exec, i)) {
0369         return false;
0370     }
0371 
0372     slot.setValue(this, jsNumber(m_bufferStart[i]));
0373     return true;
0374 }
0375 
0376 template <class T, class P>
0377 bool ArrayBufferView<T, P>::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0378 {
0379     bool ok = false;
0380     unsigned i = propertyName.toArrayIndex(&ok);
0381     if (ok) {
0382         return ArrayBufferView<T, P>::getOwnPropertySlot(exec, i, slot);
0383     }
0384 
0385     return getStaticValueSlot< ArrayBufferView<T, P>, JSObject>(exec, &ArrayBufferViewTable, this, propertyName, slot);
0386 }
0387 
0388 template <class T, class P>
0389 JSValue *ArrayBufferView<T, P>::getValueProperty(ExecState * /*exec*/, int token) const
0390 {
0391     switch (token) {
0392     case ArrayBufferViewBase::Buffer:
0393         return m_buffer;
0394     case ArrayBufferViewBase::ByteLength:
0395         return jsNumber(m_byteLength);
0396     case ArrayBufferViewBase::ByteOffset:
0397         return jsNumber(m_byteOffset);
0398     case ArrayBufferViewBase::Length:
0399         return jsNumber(m_length);
0400     default:
0401         ASSERT_NOT_REACHED();
0402         qCWarning(KHTML_LOG) << "ArrayBufferView<T>::getValueProperty unhandled token " << token;
0403         break;
0404     }
0405     return nullptr;
0406 }
0407 
0408 template <class T, class P>
0409 void ArrayBufferView<T, P>::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
0410 {
0411     bool ok = false;
0412     unsigned i = propertyName.toArrayIndex(&ok);
0413     if (ok) {
0414         ArrayBufferView<T, P>::put(exec, i, value, attr);
0415     } else {
0416         KJS::JSObject::put(exec, propertyName, value, attr);
0417     }
0418 }
0419 
0420 template <class T, class P>
0421 void ArrayBufferView<T, P>::put(ExecState *exec, unsigned int i, JSValue *value, int /*attr*/)
0422 {
0423     if (!checkIndex(exec, i)) {
0424         return;
0425     }
0426     if (value && value->type() != NumberType) {
0427         return;
0428     }
0429     m_bufferStart[i] = static_cast<T>(value->getNumber());
0430 }
0431 
0432 template <class T, class P>
0433 bool ArrayBufferView<T, P>::checkIndex(KJS::ExecState * /*exec*/, unsigned int pos)
0434 {
0435     if (m_byteOffset + (pos + 1)*sizeof(T) > m_buffer->byteLength()) {
0436         return false;
0437     }
0438     if (pos * sizeof(T) >= m_byteLength) {
0439         return false;
0440     }
0441     return true;
0442 }
0443 
0444 } // namespace KJS
0445 
0446 #endif //KJS_ARRAYBUFFERVIEW_H