QScriptValue TypedArrayPrototype::subarray(qint32 begin) {
    TypedArray* typedArray = static_cast<TypedArray*>(parent());
    QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
    qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32();
    qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32();
    qint32 bytesPerElement = typedArray->_bytesPerElement;
    
    // if indices < 0 then they start from the end of the array
    begin = (begin < 0) ? length + begin : begin;
    
    // here we clamp the indices to fit the array
    begin = glm::clamp(begin, 0, (length - 1));
    
    byteOffset += begin * bytesPerElement;
    return typedArray->newInstance(arrayBuffer, byteOffset, length - begin);
}
QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) {
    TypedArray* typedArray = static_cast<TypedArray*>(parent());
    QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
    qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32();
    qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32();
    qint32 bytesPerElement = typedArray->_bytesPerElement;
    
    // if indices < 0 then they start from the end of the array
    begin = (begin < 0) ? length + begin : begin;
    end = (end < 0) ? length + end : end;
    
    // here we clamp the indices to fit the array
    // note: begin offset is *inclusive* while end offset is *exclusive*
    // (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray#Parameters)
    begin = glm::clamp(begin, 0, (length - 1));
    end = glm::clamp(end, 0, length);
    
    byteOffset += begin * bytesPerElement;
    length = (end - begin > 0) ? end - begin : 0;
    return typedArray->newInstance(arrayBuffer, byteOffset, length);
}
Example #3
0
QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) {
    TypedArray* cls = qscriptvalue_cast<TypedArray*>(context->callee().data());
    if (!cls) {
        return QScriptValue();
    }
    if (context->argumentCount() == 0) {
        return cls->newInstance(0);
    }
    
    QScriptValue newObject;
    QScriptValue bufferArg = context->argument(0);
    QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(bufferArg.data());
    
    // parse arguments
    if (arrayBuffer) {
        if (context->argumentCount() == 1) {
            // Case for entire ArrayBuffer
            newObject = cls->newInstance(bufferArg, 0, arrayBuffer->size());
        } else {
            QScriptValue byteOffsetArg = context->argument(1);
            if (!byteOffsetArg.isNumber()) {
                engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\"");
                return QScriptValue();
            }
            if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) {
                engine->evaluate("throw \"RangeError: byteOffset out of range\"");
                return QScriptValue();
            }
            if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) {
                engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\"");
            }
            quint32 byteOffset = byteOffsetArg.toInt32();
            
            if (context->argumentCount() == 2) {
                // case for end of ArrayBuffer
                if ((arrayBuffer->size() - byteOffset) % cls->_bytesPerElement != 0) {
                    engine->evaluate("throw \"RangeError: byteLength - byteOffset not a multiple of BYTES_PER_ELEMENT\"");
                }
                quint32 length = (arrayBuffer->size() - byteOffset) / cls->_bytesPerElement;
                newObject = cls->newInstance(bufferArg, byteOffset, length);
            } else {
                
                QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue();
                if (!lengthArg.isNumber()) {
                    engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\"");
                    return QScriptValue();
                }
                if (lengthArg.toInt32() < 0 ||
                    byteOffsetArg.toInt32() + lengthArg.toInt32() * (qint32)(cls->_bytesPerElement) > arrayBuffer->size()) {
                    engine->evaluate("throw \"RangeError: byteLength out of range\"");
                    return QScriptValue();
                }
                quint32 length = lengthArg.toInt32();
                
                // case for well-defined range
                newObject = cls->newInstance(bufferArg, byteOffset, length);
            }
        }
    } else if (context->argument(0).isNumber()) {
        // case for new ArrayBuffer
        newObject = cls->newInstance(context->argument(0).toInt32());
    } else {
        newObject = cls->newInstance(bufferArg);
    }
    
    if (context->isCalledAsConstructor()) {
        // if called with the new keyword, replace this object
        context->setThisObject(newObject);
        return engine->undefinedValue();
    }
    
    return newObject;
}