File indexing completed on 2023-09-24 04:06: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