From f5a7953df3cb61edc6cc30175ea4f7f1c97deae6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 17 Jun 2018 10:57:21 +0200 Subject: Partial Proxy support get, set and deleteProperty proxying is implemented, the others require some more changes in our engine. Change-Id: I4dd4b154b1a582f5e36cdc9429fa049fd37d5167 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/jsruntime.pri | 2 + src/qml/jsruntime/qv4engine.cpp | 5 + src/qml/jsruntime/qv4engine_p.h | 2 + src/qml/jsruntime/qv4enginebase_p.h | 1 + src/qml/jsruntime/qv4managed.cpp | 3 + src/qml/jsruntime/qv4managed_p.h | 1 + src/qml/jsruntime/qv4proxy.cpp | 270 ++++++++++++++++++++++++++++++++++++ src/qml/jsruntime/qv4proxy_p.h | 116 ++++++++++++++++ 8 files changed, 400 insertions(+) create mode 100644 src/qml/jsruntime/qv4proxy.cpp create mode 100644 src/qml/jsruntime/qv4proxy_p.h (limited to 'src/qml/jsruntime') diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index a2408d7d2a..34eea36f0a 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -30,6 +30,7 @@ SOURCES += \ $$PWD/qv4numberobject.cpp \ $$PWD/qv4object.cpp \ $$PWD/qv4objectproto.cpp \ + $$PWD/qv4proxy.cpp \ $$PWD/qv4qmlcontext.cpp \ $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ @@ -89,6 +90,7 @@ HEADERS += \ $$PWD/qv4numberobject_p.h \ $$PWD/qv4object_p.h \ $$PWD/qv4objectproto_p.h \ + $$PWD/qv4proxy_p.h \ $$PWD/qv4qmlcontext_p.h \ $$PWD/qv4reflect_p.h \ $$PWD/qv4regexpobject_p.h \ diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a8331f153e..7cef7c8039 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -73,6 +73,7 @@ #include "qv4stringiterator_p.h" #include "qv4generatorobject_p.h" #include "qv4reflect_p.h" +#include "qv4proxy_p.h" #if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" @@ -290,6 +291,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive")); jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag")); jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables")); + jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy")); ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); Q_ASSERT(ic->d()->prototype); @@ -386,6 +388,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) classes[Class_ErrorProto] = ic->addMember(id_name()->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); + classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable()); + jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0); jsObjects[ErrorProto] = memoryManager->allocObject(classes[Class_ErrorProto]); @@ -532,6 +536,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate())); globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate())); globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate())); + globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate(rootContext()))); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits::quiet_NaN())); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index b007e65c4b..abd363adcb 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -368,6 +368,7 @@ public: Symbol_toPrimitive, Symbol_toStringTag, Symbol_unscopables, + Symbol_revokableProxy, NJSSymbols }; Value *jsSymbols; @@ -425,6 +426,7 @@ public: Symbol *symbol_toPrimitive() const { return reinterpret_cast(jsSymbols + Symbol_toPrimitive); } Symbol *symbol_toStringTag() const { return reinterpret_cast(jsSymbols + Symbol_toStringTag); } Symbol *symbol_unscopables() const { return reinterpret_cast(jsSymbols + Symbol_unscopables); } + Symbol *symbol_revokableProxy() const { return reinterpret_cast(jsSymbols + Symbol_revokableProxy); } #ifndef V4_BOOTSTRAP QIntrusiveList compilationUnits; diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 03ff25d5b5..7701cf37bc 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -115,6 +115,7 @@ struct Q_QML_EXPORT EngineBase { Class_ErrorObjectWithMessage, Class_ErrorProto, Class_QmlContextWrapper, + Class_ProxyObject, Class_Symbol, NClasses }; diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 58f5b0051f..9321b9dd64 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -118,6 +118,9 @@ QString Managed::className() const case Type_JsonObject: s = "JSON"; break; + case Type_ProxyObject: + s = "ProxyObject"; + break; case Type_MathObject: s = "Math"; break; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index f16bf3dc58..6983b52c83 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -196,6 +196,7 @@ public: Type_ArgumentsObject, Type_JsonObject, Type_MathObject, + Type_ProxyObject, Type_ExecutionContext, Type_InternalClass, diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp new file mode 100644 index 0000000000..b34af97870 --- /dev/null +++ b/src/qml/jsruntime/qv4proxy.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 "qv4proxy_p.h" +#include "qv4symbol_p.h" +#include "qv4jscall_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ProxyObject); + +void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler) +{ + Object::init(); + ExecutionEngine *e = internalClass->engine; + this->target.set(e, target->d()); + this->handler.set(e, handler->d()); +} + +ReturnedValue ProxyObject::get(const Managed *m, StringOrSymbol *name, bool *hasProperty) +{ + Scope scope(m); + const ProxyObject *o = static_cast(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_get())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isUndefined()) + return target->get(name, hasProperty); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + if (hasProperty) + *hasProperty = true; + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = name; + cdata.args[2] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast(trap.ptr)->call(cdata)); + ScopedProperty targetDesc(scope); + PropertyAttributes attributes; + target->getOwnProperty(name, &attributes, targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!trapResult->sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->value.isUndefined()) { + if (!trapResult->isUndefined()) + return scope.engine->throwTypeError(); + } + } + return trapResult->asReturnedValue(); +} + +ReturnedValue ProxyObject::getIndexed(const Managed *m, uint index, bool *hasProperty) +{ + Scope scope(m); + ScopedString name(scope, Primitive::fromUInt32(index).toString(scope.engine)); + return get(m, name, hasProperty); +} + +bool ProxyObject::put(Managed *m, StringOrSymbol *name, const Value &value) +{ + Scope scope(m); + const ProxyObject *o = static_cast(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_set())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isUndefined()) + return target->put(name, value); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 4, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = name; + cdata.args[2] = value; + cdata.args[3] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes; + target->getOwnProperty(name, &attributes, targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!value.sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->set.isUndefined()) + return scope.engine->throwTypeError(); + } + return true; +} + +bool ProxyObject::putIndexed(Managed *m, uint index, const Value &value) +{ + Scope scope(m); + ScopedString name(scope, Primitive::fromUInt32(index).toString(scope.engine)); + return put(m, name, value); +} + +bool ProxyObject::deleteProperty(Managed *m, StringOrSymbol *name) +{ + Scope scope(m); + const ProxyObject *o = static_cast(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isUndefined()) + return target->deleteProperty(name); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = name; + cdata.args[2] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes; + target->getOwnProperty(name, &attributes, targetDesc); + if (attributes == Attr_Invalid) + return true; + if (!attributes.isConfigurable()) + return scope.engine->throwTypeError(); + return true; +} + +bool ProxyObject::deleteIndexedProperty(Managed *m, uint index) +{ + Scope scope(m); + ScopedString name(scope, Primitive::fromUInt32(index).toString(scope.engine)); + return deleteProperty(m, name); +} + +//ReturnedValue ProxyObject::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +//{ + +//} + +//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +//{ + +//} + +DEFINE_OBJECT_VTABLE(Proxy); + +void Heap::Proxy::init(QV4::ExecutionContext *ctx) +{ + Heap::FunctionObject::init(ctx, QStringLiteral("Proxy")); + + Scope scope(ctx); + Scoped ctor(scope, this); + ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2); +} + +ReturnedValue Proxy::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *target = static_cast(argv); + const Object *handler = static_cast(argv + 1); + if (const ProxyObject *ptarget = target->as()) + if (!ptarget->d()->handler) + return scope.engine->throwTypeError(); + if (const ProxyObject *phandler = handler->as()) + if (!phandler->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, scope.engine->memoryManager->allocate(target, handler)); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::call(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + ScopedObject proxy(scope, Proxy::callAsConstructor(f, argv, argc)); + if (scope.hasException()) + return Encode::undefined(); + + ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke"))); + ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0)); + revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy); + + ScopedObject o(scope, scope.engine->newObject()); + ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy"))); + o->defineDefaultProperty(p, proxy); + o->defineDefaultProperty(revoke, revoker); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + Scoped proxy(scope, f->get(scope.engine->symbol_revokableProxy())); + Q_ASSERT(proxy); + + proxy->d()->target.set(scope.engine, nullptr); + proxy->d()->handler.set(scope.engine, nullptr); + return Encode::undefined(); +} diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h new file mode 100644 index 0000000000..52b1fa549e --- /dev/null +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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 QV4PROXY_P_H +#define QV4PROXY_P_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" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define ProxyObjectMembers(class, Member) \ + Member(class, Pointer, Object *, target) \ + Member(class, Pointer, Object *, handler) + +DECLARE_HEAP_OBJECT(ProxyObject, Object) { + DECLARE_MARKOBJECTS(ProxyObject) + + void init(const QV4::Object *target, const QV4::Object *handler); +}; + +#define ProxyMembers(class, Member) \ + Member(class, Pointer, Symbol *, revokableProxySymbol) \ + +DECLARE_HEAP_OBJECT(Proxy, FunctionObject) { + DECLARE_MARKOBJECTS(Proxy) + + void init(QV4::ExecutionContext *ctx); +}; + +} + +struct ProxyObject: Object { + V4_OBJECT2(ProxyObject, Object) + Q_MANAGED_TYPE(ProxyObject) + V4_INTERNALCLASS(ProxyObject) + + static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); + static bool put(Managed *m, StringOrSymbol *name, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); + static bool deleteProperty(Managed *m, StringOrSymbol *name); + static bool deleteIndexedProperty(Managed *m, uint index); + + // those might require a second proxy object that derives from FunctionObject... +// static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); +// static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct Proxy : FunctionObject +{ + V4_OBJECT2(Proxy, FunctionObject) + + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revocable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revoke(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H -- cgit v1.2.3