aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-23 16:06:26 +0200
committerLars Knoll <lars.knoll@qt.io>2018-08-23 19:18:19 +0000
commit732c25029ec95feb27a607ef19bb3b7423a955a1 (patch)
treec53e44cf7b7cd4555bc34aedf0474ca128292d40 /src
parentf15cc9f1df8e17f049c111e3147d6c63c07eb756 (diff)
Implement support for call/callAsConstructor in Proxy objects
This adds the last missing piece of functionality for Proxy objects. Also fix a bug where we ignored the newTarget in Reflect.construct. Change-Id: I2443470f2ca13fb6223768c3bf6bdc3766bb4fc3 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp1
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h1
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp90
-rw-r--r--src/qml/jsruntime/qv4proxy_p.h33
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp8
5 files changed, 116 insertions, 17 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 57e93895fc..5ccf3d4f6e 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -402,6 +402,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
Q_ASSERT(index == ErrorPrototype::Index_Name);
classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable());
+ classes[Class_ProxyFunctionObject] = classes[Class_Empty]->changeVTable(ProxyFunctionObject::staticVTable());
jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0);
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index 189208e731..789ec5d970 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -118,6 +118,7 @@ struct Q_QML_EXPORT EngineBase {
Class_ErrorProto,
Class_QmlContextWrapper,
Class_ProxyObject,
+ Class_ProxyFunctionObject,
Class_Symbol,
NClasses
};
diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp
index e26c51b473..d4f342c50e 100644
--- a/src/qml/jsruntime/qv4proxy.cpp
+++ b/src/qml/jsruntime/qv4proxy.cpp
@@ -48,6 +48,7 @@
using namespace QV4;
DEFINE_OBJECT_VTABLE(ProxyObject);
+DEFINE_OBJECT_VTABLE(ProxyFunctionObject);
void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler)
{
@@ -57,6 +58,18 @@ void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handl
this->handler.set(e, handler->d());
}
+void Heap::ProxyFunctionObject::init(const QV4::FunctionObject *target, const QV4::Object *handler)
+{
+ ExecutionEngine *e = internalClass->engine;
+ FunctionObject::init(e->rootContext());
+ this->target.set(e, target->d());
+ this->handler.set(e, handler->d());
+
+ if (!target->isConstructor())
+ jsConstruct = nullptr;
+}
+
+
ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Scope scope(m);
@@ -631,15 +644,68 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m)
}
-//ReturnedValue ProxyObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
-//{
+ReturnedValue ProxyFunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ Scope scope(f);
+ const ProxyObject *o = static_cast<const ProxyObject *>(f);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
-//}
+ ScopedFunctionObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("construct")));
+ ScopedValue trap(scope, handler->get(name));
-//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
-//{
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined()) {
+ Q_ASSERT(target->isConstructor());
+ return target->callAsConstructor(argv, argc, newTarget);
+ }
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
-//}
+ ScopedFunctionObject trapFunction(scope, trap);
+ Value *arguments = scope.alloc(3);
+ arguments[0] = target;
+ arguments[1] = scope.engine->newArrayObject(argv, argc);
+ arguments[2] = newTarget ? *newTarget : Primitive::undefinedValue();
+ ScopedObject result(scope, trapFunction->call(handler, arguments, 3));
+
+ if (!result)
+ return scope.engine->throwTypeError();
+ return result->asReturnedValue();
+}
+
+ReturnedValue ProxyFunctionObject::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+
+ const ProxyObject *o = static_cast<const ProxyObject *>(f);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("apply")));
+ ScopedValue trap(scope, handler->get(name));
+
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->call(thisObject, argv, argc);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject trapFunction(scope, trap);
+ Value *arguments = scope.alloc(3);
+ arguments[0] = target;
+ arguments[1] = thisObject ? *thisObject : Primitive::undefinedValue();
+ arguments[2] = scope.engine->newArrayObject(argv, argc);
+ return trapFunction->call(handler, arguments, 3);
+}
DEFINE_OBJECT_VTABLE(Proxy);
@@ -668,8 +734,10 @@ ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Val
if (!phandler->d()->handler)
return scope.engine->throwTypeError();
- ScopedObject o(scope, scope.engine->memoryManager->allocate<ProxyObject>(target, handler));
- return o->asReturnedValue();
+ const FunctionObject *targetFunction = target->as<FunctionObject>();
+ if (targetFunction)
+ return scope.engine->memoryManager->allocate<ProxyFunctionObject>(targetFunction, handler)->asReturnedValue();
+ return scope.engine->memoryManager->allocate<ProxyObject>(target, handler)->asReturnedValue();
}
ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
@@ -683,6 +751,7 @@ ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, co
ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f));
if (scope.hasException())
return Encode::undefined();
+ Q_ASSERT(proxy);
ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke")));
ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0));
@@ -698,8 +767,9 @@ ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, co
ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int)
{
Scope scope(f);
- Scoped<ProxyObject> proxy(scope, f->get(scope.engine->symbol_revokableProxy()));
- Q_ASSERT(proxy);
+ ScopedObject o(scope, f->get(scope.engine->symbol_revokableProxy()));
+ Q_ASSERT(o);
+ ProxyObject *proxy = o->cast<ProxyObject>();
proxy->d()->target.set(scope.engine, nullptr);
proxy->d()->handler.set(scope.engine, nullptr);
diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h
index bad4d040c7..7c10b91b13 100644
--- a/src/qml/jsruntime/qv4proxy_p.h
+++ b/src/qml/jsruntime/qv4proxy_p.h
@@ -63,12 +63,16 @@ namespace Heap {
Member(class, Pointer, Object *, target) \
Member(class, Pointer, Object *, handler)
-DECLARE_HEAP_OBJECT(ProxyObject, Object) {
+DECLARE_HEAP_OBJECT(ProxyObject, FunctionObject) {
DECLARE_MARKOBJECTS(ProxyObject)
void init(const QV4::Object *target, const QV4::Object *handler);
};
+struct ProxyFunctionObject : ProxyObject {
+ void init(const QV4::FunctionObject *target, const QV4::Object *handler);
+};
+
#define ProxyMembers(class, Member) \
Member(class, Pointer, Symbol *, revokableProxySymbol) \
@@ -80,10 +84,21 @@ DECLARE_HEAP_OBJECT(Proxy, FunctionObject) {
}
-struct ProxyObject: Object {
+/*
+ * The inheritance from FunctionObject is a hack. Regular proxy objects are no function objects.
+ * But this helps implement the proxy for function objects, where we need this and thus gives us
+ * all the virtual methods from ProxyObject without having to duplicate them.
+ *
+ * But it does require a few hacks to make sure we don't recognize regular proxy objects as function
+ * objects in the runtime.
+ */
+struct ProxyObject : FunctionObject {
V4_OBJECT2(ProxyObject, Object)
Q_MANAGED_TYPE(ProxyObject)
V4_INTERNALCLASS(ProxyObject)
+ enum {
+ IsFunctionObject = false
+ };
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
@@ -96,10 +111,18 @@ struct ProxyObject: Object {
static Heap::Object *virtualGetPrototypeOf(const Managed *);
static bool virtualSetPrototypeOf(Managed *, const Object *);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
+};
- // those might require a second proxy object that derives from 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 ProxyFunctionObject : ProxyObject {
+ V4_OBJECT2(ProxyFunctionObject, FunctionObject)
+ Q_MANAGED_TYPE(ProxyObject)
+ V4_INTERNALCLASS(ProxyFunctionObject)
+ enum {
+ IsFunctionObject = true
+ };
+
+ 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 Proxy : FunctionObject
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
index 90c39f7c37..3dc0956e0c 100644
--- a/src/qml/jsruntime/qv4reflect.cpp
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -103,7 +103,11 @@ ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, cons
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())
+ if (argc < 2 || !argv[1].isObject())
+ return scope.engine->throwTypeError();
+ const FunctionObject *target = argv[0].as<FunctionObject>();
+ const FunctionObject *newTarget = argc == 3 ? argv[2].as<FunctionObject>() : target;
+ if (!target || !target->isConstructor() || !newTarget || !newTarget->isConstructor())
return scope.engine->throwTypeError();
const Object *o = static_cast<const Object *>(argv + 1);
@@ -111,7 +115,7 @@ ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *,
if (scope.hasException())
return Encode::undefined();
- return static_cast<const FunctionObject &>(argv[0]).callAsConstructor(arguments.argv, arguments.argc);
+ return target->callAsConstructor(arguments.argv, arguments.argc, newTarget);
}
ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)