aboutsummaryrefslogtreecommitdiffstats
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
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>
-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
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations18
6 files changed, 116 insertions, 35 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)
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index 1a29d0b206..106b16f64a 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -262,7 +262,6 @@ built-ins/Function/prototype/toString/async-method-class-statement.js fails
built-ins/Function/prototype/toString/async-method-object.js fails
built-ins/Function/prototype/toString/intrinsics.js fails
built-ins/Function/prototype/toString/method-computed-property-name.js fails
-built-ins/Function/prototype/toString/proxy.js fails
built-ins/GeneratorFunction/proto-from-ctor-realm.js fails
built-ins/JSON/parse/revived-proxy-revoked.js fails
built-ins/JSON/parse/revived-proxy.js fails
@@ -483,24 +482,10 @@ built-ins/Promise/resolve/prop-desc.js fails
built-ins/Promise/resolve/resolve-from-promise-capability.js fails
built-ins/Promise/resolve/resolve-prms-cstm-then.js fails
built-ins/Proxy/apply/arguments-realm.js fails
-built-ins/Proxy/apply/call-parameters.js fails
-built-ins/Proxy/apply/call-result.js fails
-built-ins/Proxy/apply/return-abrupt.js fails
built-ins/Proxy/apply/trap-is-not-callable-realm.js fails
-built-ins/Proxy/apply/trap-is-null.js fails
-built-ins/Proxy/apply/trap-is-undefined-no-property.js fails
-built-ins/Proxy/apply/trap-is-undefined.js fails
built-ins/Proxy/construct/arguments-realm.js fails
-built-ins/Proxy/construct/call-parameters-new-target.js fails
-built-ins/Proxy/construct/call-parameters.js fails
-built-ins/Proxy/construct/call-result.js fails
-built-ins/Proxy/construct/return-is-abrupt.js fails
built-ins/Proxy/construct/trap-is-not-callable-realm.js fails
-built-ins/Proxy/construct/trap-is-null.js fails
-built-ins/Proxy/construct/trap-is-undefined-no-property.js fails
built-ins/Proxy/construct/trap-is-undefined-proto-from-ctor-realm.js fails
-built-ins/Proxy/construct/trap-is-undefined.js fails
-built-ins/Proxy/create-target-is-not-constructor.js fails
built-ins/Proxy/defineProperty/desc-realm.js fails
built-ins/Proxy/defineProperty/null-handler-realm.js fails
built-ins/Proxy/defineProperty/targetdesc-configurable-desc-not-configurable-realm.js fails
@@ -541,8 +526,6 @@ built-ins/Proxy/revocable/revocation-function-nonconstructor.js fails
built-ins/Proxy/set/trap-is-not-callable-realm.js fails
built-ins/Proxy/set/trap-is-undefined-receiver.js fails
built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js fails
-built-ins/Reflect/construct/newtarget-is-not-constructor-throws.js fails
-built-ins/Reflect/construct/return-with-newtarget-argument.js fails
built-ins/Reflect/set/creates-a-data-descriptor.js fails
built-ins/Reflect/set/different-property-descriptors.js fails
built-ins/Reflect/set/receiver-is-not-object.js fails
@@ -1224,7 +1207,6 @@ language/expressions/generators/yield-identifier-non-strict.js sloppyFails
language/expressions/generators/yield-star-before-newline.js fails
language/expressions/logical-and/tco-right.js strictFails
language/expressions/logical-or/tco-right.js strictFails
-language/expressions/new.target/value-via-reflect-construct.js fails
language/expressions/new.target/value-via-super-call.js fails
language/expressions/new.target/value-via-super-property.js fails
language/expressions/new/non-ctor-err-realm.js fails