From 12bc11e5af20e68c504ab56de8ef0e0b76efd12c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 15 Jun 2018 23:00:32 +0200 Subject: Add support for Reflect Implemented all methods in Reflect, only some smaller bugs left in there. Change-Id: I53d2304d0e59566aec64e200cd995e02afcfc33e Reviewed-by: Simon Hausmann --- src/qml/jsruntime/jsruntime.pri | 2 + src/qml/jsruntime/qv4engine.cpp | 2 + src/qml/jsruntime/qv4internalclass.cpp | 2 +- src/qml/jsruntime/qv4reflect.cpp | 270 +++++++++++++++++++++++++++++++++ src/qml/jsruntime/qv4reflect_p.h | 89 +++++++++++ 5 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 src/qml/jsruntime/qv4reflect.cpp create mode 100644 src/qml/jsruntime/qv4reflect_p.h (limited to 'src') diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 97610a0e40..a2408d7d2a 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -31,6 +31,7 @@ SOURCES += \ $$PWD/qv4object.cpp \ $$PWD/qv4objectproto.cpp \ $$PWD/qv4qmlcontext.cpp \ + $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ $$PWD/qv4stringiterator.cpp \ $$PWD/qv4stringobject.cpp \ @@ -89,6 +90,7 @@ HEADERS += \ $$PWD/qv4object_p.h \ $$PWD/qv4objectproto_p.h \ $$PWD/qv4qmlcontext_p.h \ + $$PWD/qv4reflect_p.h \ $$PWD/qv4regexpobject_p.h \ $$PWD/qv4runtimecodegen_p.h \ $$PWD/qv4stringiterator_p.h \ diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 6e8dfbc09c..a8331f153e 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -72,6 +72,7 @@ #include "qv4iterator_p.h" #include "qv4stringiterator_p.h" #include "qv4generatorobject_p.h" +#include "qv4reflect_p.h" #if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" @@ -530,6 +531,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ScopedObject o(scope); globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate())); globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate())); + globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate())); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits::quiet_NaN())); diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index da37fda590..a913d5ca75 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -294,7 +294,7 @@ Heap::InternalClass *InternalClass::changeMember(Identifier identifier, Property for (uint i = 0; i < size; ++i) { Identifier identifier = nameMap.at(i); PropertyHash::Entry e = { identifier, newClass->size }; - if (!identifier.isValid()) + if (i && !identifier.isValid()) e.identifier = nameMap.at(i - 1); newClass->propertyTable.addEntry(e, newClass->size); newClass->nameMap.add(newClass->size, identifier); diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp new file mode 100644 index 0000000000..69baecd337 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** 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. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4reflect_p.h" +#include "qv4symbol_p.h" +#include "qv4runtimeapi_p.h" +#include "qv4objectproto_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(Reflect); + +void Heap::Reflect::init() +{ + Object::init(); + Scope scope(internalClass->engine); + ScopedObject r(scope, this); + + r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3); + r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2); + r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3); + r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2); + r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2); + r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2); + r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1); + r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2); + r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1); + r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1); + r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1); + r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3); + r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2); +} + +struct CallArgs { + Value *argv; + int argc; +}; + +static CallArgs createListFromArrayLike(Scope &scope, const Object *o) +{ + int len = o->getLength(); + Value *arguments = scope.alloc(len); + + for (int i = 0; i < len; ++i) { + arguments[i] = o->getIndexed(i); + if (scope.hasException()) + return { nullptr, 0 }; + } + return { arguments, len }; +} + +ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject()) + return scope.engine->throwTypeError(); + + const Object *o = static_cast(argv + 2); + CallArgs arguments = createListFromArrayLike(scope, o); + if (scope.hasException()) + return Encode::undefined(); + + return static_cast(argv[0]).call(&argv[1], arguments.argv, arguments.argc); +} + +ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isFunctionObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *o = static_cast(argv + 1); + CallArgs arguments = createListFromArrayLike(scope, o); + if (scope.hasException()) + return Encode::undefined(); + + return static_cast(argv[0]).callAsConstructor(arguments.argv, arguments.argc); +} + +ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0]); + ScopedStringOrSymbol name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue()); + ScopedProperty pd(scope); + PropertyAttributes attrs; + ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + bool result = O->__defineOwnProperty__(scope.engine, name, pd, attrs); + + return Encode(result); +} + +ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!argc || !argv[0].isObject()) + return e->throwTypeError(); + + bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Primitive::undefinedValue()); + return Encode(result); +} + +ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + // ### Fix third receiver argument + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + + uint n = index->asArrayIndex(); + if (n < UINT_MAX) + return Encode(o->getIndexed(n)); + + ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + return Encode(o->get(name)); +} + +ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getPrototypeOf(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + + bool hasProperty = false; + uint n = index->asArrayIndex(); + if (n < UINT_MAX) { + (void) o->getIndexed(n, &hasProperty); + return Encode(hasProperty); + } + + ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + (void) o->get(name, &hasProperty); + return Encode(hasProperty); +} + +ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + const Object *o = static_cast(argv); + return Encode(o->isExtensible()); +} + + +ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getOwnPropertyNames(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast(argv)); + o->setInternalClass(o->internalClass()->nonExtensible()); + return Encode(true); +} + +ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + // ### Fix third receiver argument + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + const Value &val = argc > 2 ? argv[2] : undef; + + uint n = index->asArrayIndex(); + if (n < UINT_MAX) { + bool result = o->putIndexed(n, val); + return Encode(result); + } + + ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + bool result = o->put(name, val); + return Encode(result); +} + +ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (argc < 2 || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_setPrototypeOf(f, thisObject, argv, argc); +} diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h new file mode 100644 index 0000000000..73d257e006 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REFLECT_H +#define QV4REFLECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +struct Reflect : Object { + void init(); +}; + +} + +struct Reflect : Object { + V4_OBJECT2(Reflect, Object) + + static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_construct(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3