From 860807b22ab4f7d1c55ce69bb7711dcc777ceefa Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 24 Aug 2018 12:03:12 +0200 Subject: Implement support for SharedArrayBuffer We'll still need to hook this up with Worker threads to become useful. Change-Id: Iedae7307edd76368aeba163731856ebe9b32c6b6 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraybuffer.cpp | 120 ++++++++++++++++++++++++++--------- src/qml/jsruntime/qv4arraybuffer_p.h | 72 +++++++++++++++++---- src/qml/jsruntime/qv4dataview.cpp | 2 +- src/qml/jsruntime/qv4dataview_p.h | 2 +- src/qml/jsruntime/qv4engine.cpp | 5 ++ src/qml/jsruntime/qv4engine_p.h | 4 ++ src/qml/jsruntime/qv4global_p.h | 2 + 7 files changed, 164 insertions(+), 43 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index dc8bce12a0..9b5a983fdf 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -45,14 +45,46 @@ using namespace QV4; +DEFINE_OBJECT_VTABLE(SharedArrayBufferCtor); DEFINE_OBJECT_VTABLE(ArrayBufferCtor); +DEFINE_OBJECT_VTABLE(SharedArrayBuffer); DEFINE_OBJECT_VTABLE(ArrayBuffer); +void Heap::SharedArrayBufferCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("SharedArrayBuffer")); +} + void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) { Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer")); } +ReturnedValue SharedArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +{ + Scope scope(f); + if (newTarget->isUndefined()) + return scope.engine->throwTypeError(); + + qint64 len = argc ? argv[0].toIndex() : 0; + if (scope.engine->hasException) + return Encode::undefined(); + if (len < 0 || len >= INT_MAX) + return scope.engine->throwRangeError(QStringLiteral("SharedArrayBuffer: Invalid length.")); + + Scoped a(scope, scope.engine->memoryManager->allocate(len)); + if (scope.engine->hasException) + return Encode::undefined(); + + return a->asReturnedValue(); +} + +ReturnedValue SharedArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + + ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = f->engine(); @@ -73,12 +105,6 @@ ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, return a->asReturnedValue(); } - -ReturnedValue ArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc) { if (argc < 1) @@ -92,7 +118,7 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value } -void Heap::ArrayBuffer::init(size_t length) +void Heap::SharedArrayBuffer::init(size_t length) { Object::init(); if (length < UINT_MAX) @@ -103,16 +129,18 @@ void Heap::ArrayBuffer::init(size_t length) } data->size = int(length); memset(data->data(), 0, length + 1); + isShared = true; } -void Heap::ArrayBuffer::init(const QByteArray& array) +void Heap::SharedArrayBuffer::init(const QByteArray& array) { Object::init(); data = const_cast(array).data_ptr(); data->ref.ref(); + isShared = true; } -void Heap::ArrayBuffer::destroy() +void Heap::SharedArrayBuffer::destroy() { if (data && !data->ref.deref()) QTypedArrayData::deallocate(data); @@ -145,66 +173,100 @@ void ArrayBuffer::detach() { } -void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) +void SharedArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); ctor->addSymbolSpecies(); defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); - ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer"))); + ScopedString name(scope, engine->newString(QStringLiteral("SharedArrayBuffer"))); defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); } -ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue SharedArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { - const ArrayBuffer *a = thisObject->as(); - if (!a || a->isDetachedBuffer() || a->isSharedArrayBuffer()) + const SharedArrayBuffer *a = thisObject->as(); + if (!a || a->isDetachedBuffer() || !a->isSharedArrayBuffer()) return b->engine()->throwTypeError(); return Encode(a->d()->data->size); } -ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue SharedArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ExecutionEngine *v4 = b->engine(); - const ArrayBuffer *a = thisObject->as(); - if (!a || a->isDetachedBuffer() || a->isSharedArrayBuffer()) - return v4->throwTypeError(); + return slice(b, thisObject, argv, argc, true); +} + +ReturnedValue SharedArrayBufferPrototype::slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, bool shared) +{ + Scope scope(b); + const SharedArrayBuffer *a = thisObject->as(); + if (!a || a->isDetachedBuffer() || (a->isSharedArrayBuffer() != shared)) + return scope.engine->throwTypeError(); double start = argc > 0 ? argv[0].toInteger() : 0; double end = (argc < 2 || argv[1].isUndefined()) ? a->d()->data->size : argv[1].toInteger(); - if (v4->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); - Scope scope(v4); - const FunctionObject *constructor = a->speciesConstructor(scope, scope.engine->arrayBufferCtor()); + const FunctionObject *constructor = a->speciesConstructor(scope, shared ? scope.engine->sharedArrayBufferCtor() : scope.engine->arrayBufferCtor()); if (!constructor) - return v4->throwTypeError(); + return scope.engine->throwTypeError(); double newLen = qMax(final - first, 0.); ScopedValue argument(scope, QV4::Encode(newLen)); - QV4::Scoped newBuffer(scope, constructor->callAsConstructor(argument, 1)); + QV4::Scoped newBuffer(scope, constructor->callAsConstructor(argument, 1)); if (!newBuffer || newBuffer->d()->data->size < (int)newLen || - newBuffer->isDetachedBuffer() || newBuffer->isSharedArrayBuffer() || + newBuffer->isDetachedBuffer() || (newBuffer->isSharedArrayBuffer() != shared) || newBuffer->sameValue(*a) || a->isDetachedBuffer()) - return v4->throwTypeError(); + return scope.engine->throwTypeError(); memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); return newBuffer->asReturnedValue(); } + +void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); + ctor->addSymbolSpecies(); + + defineDefaultProperty(engine->id_constructor(), (o = ctor)); + defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); + defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); + ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer"))); + defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); +} + +ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + const ArrayBuffer *a = thisObject->as(); + if (!a || a->isDetachedBuffer() || a->isSharedArrayBuffer()) + return f->engine()->throwTypeError(); + + return Encode(a->d()->data->size); +} + +ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + return slice(b, thisObject, argv, argc, false); +} + ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 036d141c2f..8344fa2554 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -59,59 +59,107 @@ namespace QV4 { namespace Heap { -struct ArrayBufferCtor : FunctionObject { +struct SharedArrayBufferCtor : FunctionObject { void init(QV4::ExecutionContext *scope); }; -struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object { +struct ArrayBufferCtor : SharedArrayBufferCtor { + void init(QV4::ExecutionContext *scope); +}; + +struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object { void init(size_t length); void init(const QByteArray& array); void destroy(); QTypedArrayData *data; + bool isShared; uint byteLength() const { return data ? data->size : 0; } + bool isDetachedBuffer() const { return !data; } + bool isSharedArrayBuffer() const { return isShared; } +}; + +struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer { + void init(size_t length) { + SharedArrayBuffer::init(length); + isShared = false; + } + void init(const QByteArray& array) { + SharedArrayBuffer::init(array); + isShared = false; + } void detachArrayBuffer() { if (data && !data->ref.deref()) QTypedArrayData::deallocate(data); data = nullptr; } - bool isDetachedBuffer() const { return !data; } - bool isSharedArrayBuffer() const { return false; } }; + } -struct ArrayBufferCtor: FunctionObject +struct SharedArrayBufferCtor : FunctionObject { - V4_OBJECT2(ArrayBufferCtor, FunctionObject) + V4_OBJECT2(SharedArrayBufferCtor, FunctionObject) static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct ArrayBufferCtor : SharedArrayBufferCtor +{ + V4_OBJECT2(ArrayBufferCtor, SharedArrayBufferCtor) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; +struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object +{ + V4_OBJECT2(SharedArrayBuffer, Object) + V4_NEEDS_DESTROY + V4_PROTOTYPE(sharedArrayBufferPrototype) + + QByteArray asByteArray() const; + uint byteLength() const { return d()->byteLength(); } + char *data() { Q_ASSERT(d()->data); return d()->data->data(); } + const char *constData() { Q_ASSERT(d()->data); return d()->data->data(); } + + bool isShared() { return d()->data->ref.isShared(); } + bool isDetachedBuffer() const { return !d()->data; } + bool isSharedArrayBuffer() const { return d()->isShared; } }; -struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object +struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer { - V4_OBJECT2(ArrayBuffer, Object) + V4_OBJECT2(ArrayBuffer, SharedArrayBuffer) V4_NEEDS_DESTROY V4_PROTOTYPE(arrayBufferPrototype) QByteArray asByteArray() const; uint byteLength() const { return d()->byteLength(); } char *data() { detach(); return d()->data ? d()->data->data() : nullptr; } + // ### is that detach needed? const char *constData() { detach(); return d()->data ? d()->data->data() : nullptr; } bool isShared() { return d()->data && d()->data->ref.isShared(); } - bool isDetachedBuffer() const { return !d()->data; } - bool isSharedArrayBuffer() const { return false; } void detach(); void detachArrayBuffer() { d()->detachArrayBuffer(); } }; -struct ArrayBufferPrototype: Object +struct SharedArrayBufferPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, bool shared); +}; + +struct ArrayBufferPrototype : SharedArrayBufferPrototype { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index 3f0e2fd1a7..d52b9488ac 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -75,7 +75,7 @@ static uint toIndex(ExecutionEngine *e, const Value &v) ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { Scope scope(f->engine()); - Scoped buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); + Scoped buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!newTarget || !buffer) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 6a9f865c0f..199a6f9f80 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -64,7 +64,7 @@ struct DataViewCtor : FunctionObject { }; #define DataViewMembers(class, Member) \ - Member(class, Pointer, ArrayBuffer *, buffer) \ + Member(class, Pointer, SharedArrayBuffer *, buffer) \ Member(class, NoMark, uint, byteLength) \ Member(class, NoMark, uint, byteOffset) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5ccf3d4f6e..c1a4b67d60 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -494,6 +494,10 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) // typed arrays + jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate(global); + jsObjects[SharedArrayBufferProto] = memoryManager->allocate(); + static_cast(sharedArrayBufferPrototype())->init(this, sharedArrayBufferCtor()); + jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate(global); jsObjects[ArrayBufferProto] = memoryManager->allocate(); static_cast(arrayBufferPrototype())->init(this, arrayBufferCtor()); @@ -539,6 +543,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor()); globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor()); globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor()); globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor()); globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor()); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index e11e607bd1..0f4a4a75a2 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -162,6 +162,7 @@ public: #if QT_CONFIG(qml_sequence_object) SequenceProto, #endif + SharedArrayBufferProto, ArrayBufferProto, DataViewProto, SetProto, @@ -193,6 +194,7 @@ public: SyntaxError_Ctor, TypeError_Ctor, URIError_Ctor, + SharedArrayBuffer_Ctor, ArrayBuffer_Ctor, DataView_Ctor, Set_Ctor, @@ -229,6 +231,7 @@ public: FunctionObject *syntaxErrorCtor() const { return reinterpret_cast(jsObjects + SyntaxError_Ctor); } FunctionObject *typeErrorCtor() const { return reinterpret_cast(jsObjects + TypeError_Ctor); } FunctionObject *uRIErrorCtor() const { return reinterpret_cast(jsObjects + URIError_Ctor); } + FunctionObject *sharedArrayBufferCtor() const { return reinterpret_cast(jsObjects + SharedArrayBuffer_Ctor); } FunctionObject *arrayBufferCtor() const { return reinterpret_cast(jsObjects + ArrayBuffer_Ctor); } FunctionObject *dataViewCtor() const { return reinterpret_cast(jsObjects + DataView_Ctor); } FunctionObject *setCtor() const { return reinterpret_cast(jsObjects + Set_Ctor); } @@ -262,6 +265,7 @@ public: Object *sequencePrototype() const { return reinterpret_cast(jsObjects + SequenceProto); } #endif + Object *sharedArrayBufferPrototype() const { return reinterpret_cast(jsObjects + SharedArrayBufferProto); } Object *arrayBufferPrototype() const { return reinterpret_cast(jsObjects + ArrayBufferProto); } Object *dataViewPrototype() const { return reinterpret_cast(jsObjects + DataViewProto); } Object *setPrototype() const { return reinterpret_cast(jsObjects + SetProto); } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index e3b3423a9d..b4dac505cd 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -190,6 +190,7 @@ namespace Heap { struct RegExp; struct EvalFunction; + struct SharedArrayBuffer; struct ArrayBuffer; struct DataView; struct TypedArray; @@ -236,6 +237,7 @@ struct RegExpObject; struct RegExp; struct EvalFunction; +struct SharedArrayBuffer; struct ArrayBuffer; struct DataView; struct TypedArray; -- cgit v1.2.3