diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-08-23 16:06:26 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-08-23 19:18:19 +0000 |
commit | 732c25029ec95feb27a607ef19bb3b7423a955a1 (patch) | |
tree | c53e44cf7b7cd4555bc34aedf0474ca128292d40 | |
parent | f15cc9f1df8e17f049c111e3147d6c63c07eb756 (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.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4enginebase_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4proxy.cpp | 90 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4proxy_p.h | 33 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4reflect.cpp | 8 | ||||
-rw-r--r-- | tests/auto/qml/ecmascripttests/TestExpectations | 18 |
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 |