aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-06-19 13:28:26 +0200
committerLars Knoll <lars.knoll@qt.io>2018-06-25 07:36:48 +0000
commit1596112e146b28541bcee412ed159cdea7e692d0 (patch)
tree29e858041496256c9c9c700fca4fb7220d78d46e /src
parent3823e9b94f849f8344b81250fac708ac2eaccd16 (diff)
Add Proxy support for prototype handling
Cleanup get/setPrototypeOf and fix some smaller incompatibilities in the default implementation for Object. Add the methods to the vtable and reimplement them according to spec for ProxyObjects. Clean up the Object.prototype.get/setPrototypeOf/__proto__ methods and fix a smaller bug in the Reflect API for those methods. Change-Id: I6e438753332ec4db963d6cdcf86f340ff212777a Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/imports/localstorage/plugin.cpp8
-rw-r--r--src/particles/qquickv4particledata.cpp2
-rw-r--r--src/qml/jsapi/qjsvalue.cpp6
-rw-r--r--src/qml/jsruntime/qv4engine.cpp6
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4object.cpp58
-rw-r--r--src/qml/jsruntime/qv4object_p.h13
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp4
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp48
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp83
-rw-r--r--src/qml/jsruntime/qv4proxy_p.h2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp15
-rw-r--r--src/qml/jsruntime/qv4reflect_p.h2
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp4
-rw-r--r--src/qml/qml/qqmlcomponent.cpp2
-rw-r--r--src/qml/qml/qqmllocale.cpp2
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp24
-rw-r--r--src/qml/qml/v8/qv8engine.cpp4
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp4
-rw-r--r--src/qml/util/qqmladaptormodel.cpp4
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp10
22 files changed, 205 insertions, 106 deletions
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index fb20a1c457..60c1f89c9b 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -323,7 +323,7 @@ static ReturnedValue qmlsqldatabase_executeSql(const FunctionObject *b, const Va
if (query.exec()) {
QV4::Scoped<QQmlSqlDatabaseWrapper> rows(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->rowsProto.value());
- rows->setPrototype(p.getPointer());
+ rows->setPrototypeUnchecked(p.getPointer());
rows->d()->type = Heap::QQmlSqlDatabaseWrapper::Rows;
*rows->d()->database = db;
*rows->d()->sqlQuery = query;
@@ -397,7 +397,7 @@ static ReturnedValue qmlsqldatabase_changeVersion(const FunctionObject *b, const
Scoped<QQmlSqlDatabaseWrapper> w(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
ScopedObject p(scope, databaseData(scope.engine)->queryProto.value());
- w->setPrototype(p.getPointer());
+ w->setPrototypeUnchecked(p.getPointer());
w->d()->type = Heap::QQmlSqlDatabaseWrapper::Query;
*w->d()->database = db;
*w->d()->version = *r->d()->version;
@@ -449,7 +449,7 @@ static ReturnedValue qmlsqldatabase_transaction_shared(const FunctionObject *b,
Scoped<QQmlSqlDatabaseWrapper> w(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->queryProto.value());
- w->setPrototype(p.getPointer());
+ w->setPrototypeUnchecked(p.getPointer());
w->d()->type = Heap::QQmlSqlDatabaseWrapper::Query;
*w->d()->database = db;
*w->d()->version = *r->d()->version;
@@ -773,7 +773,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
QV4::Scoped<QQmlSqlDatabaseWrapper> db(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->databaseProto.value());
- db->setPrototype(p.getPointer());
+ db->setPrototypeUnchecked(p.getPointer());
*db->d()->database = database;
*db->d()->version = version;
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index cf05245f5b..6d43b3880b 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -520,7 +520,7 @@ QQuickV4ParticleData::QQuickV4ParticleData(QV4::ExecutionEngine* v4, QQuickParti
QV4ParticleDataDeletable *d = particleV4Data(scope.engine);
QV4::ScopedObject o(scope, v4->memoryManager->allocate<QV4ParticleData>(datum, system));
QV4::ScopedObject p(scope, d->proto.value());
- o->setPrototype(p);
+ o->setPrototypeUnchecked(p);
m_v4Value = o;
}
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index aaac4e0a9a..01ec44e250 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -852,7 +852,7 @@ QJSValue QJSValue::prototype() const
ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<QV4::Object>());
if (!o)
return QJSValue();
- ScopedObject p(scope, o->prototype());
+ ScopedObject p(scope, o->getPrototypeOf());
if (!p)
return QJSValue(NullValue);
return QJSValue(o->internalClass()->engine, p.asReturnedValue());
@@ -884,7 +884,7 @@ void QJSValue::setPrototype(const QJSValue& prototype)
if (!val)
return;
if (val->isNull()) {
- o->setPrototype(nullptr);
+ o->setPrototypeOf(nullptr);
return;
}
@@ -895,7 +895,7 @@ void QJSValue::setPrototype(const QJSValue& prototype)
qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
return;
}
- if (!o->setPrototype(p))
+ if (!o->setPrototypeOf(p))
qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
}
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 7cef7c8039..5f202b8a24 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -402,7 +402,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
- Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d());
+ Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
#if QT_CONFIG(qml_sequence_object)
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
@@ -1849,7 +1849,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
} else if (Object *o = value->objectValue()) {
// Look in the prototype chain.
QV4::Scope scope(this);
- QV4::ScopedObject proto(scope, o->prototype());
+ QV4::ScopedObject proto(scope, o->getPrototypeOf());
while (proto) {
bool canCast = false;
if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
@@ -1870,7 +1870,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
*reinterpret_cast<void* *>(data) = var.data();
return true;
}
- proto = proto->prototype();
+ proto = proto->getPrototypeOf();
}
}
} else if (value->isNull() && name.endsWith('*')) {
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index a29eef513c..1c4c970c2e 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -78,9 +78,9 @@ Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Funct
Scope scope(context);
Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function));
ScopedObject proto(scope, scope.engine->newObject());
- proto->setPrototype(scope.engine->generatorPrototype());
+ proto->setPrototypeOf(scope.engine->generatorPrototype());
g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
- g->setPrototype(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
return g->d();
}
@@ -104,7 +104,7 @@ ReturnedValue GeneratorFunction::call(const FunctionObject *f, const Value *this
Scope scope(gf);
Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject]));
- g->setPrototype(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
+ g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
Heap::GeneratorObject *gp = g->d();
gp->stack.size = stackSize;
@@ -160,7 +160,7 @@ void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable);
ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable);
- setPrototype(engine->iteratorPrototype());
+ setPrototypeOf(engine->iteratorPrototype());
defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable);
defineDefaultProperty(QStringLiteral("next"), method_next, 1);
defineDefaultProperty(QStringLiteral("return"), method_return, 1);
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 02acc1a5e0..d1f71b18ae 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -93,19 +93,6 @@ void Heap::Object::setUsedAsProto()
internalClass.set(internalClass->engine, internalClass->asProtoClass());
}
-bool Object::setPrototype(Object *proto)
-{
- Heap::Object *p = proto ? proto->d() : nullptr;
- Heap::Object *pp = p;
- while (pp) {
- if (pp == d())
- return false;
- pp = pp->prototype();
- }
- setInternalClass(internalClass()->changePrototype(p));
- return true;
-}
-
ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs)
{
if (!attrs.isAccessor())
@@ -278,6 +265,11 @@ void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttribut
}
}
+void Object::setPrototypeUnchecked(const Object *p)
+{
+ setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr));
+}
+
// Section 8.12.2
PropertyIndex Object::getValueOrSetter(StringOrSymbol *name, PropertyAttributes *attrs)
{
@@ -484,7 +476,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
return str.asReturnedValue();
}
}
- o = o->prototype();
+ o = o->getPrototypeOf();
}
if (exists) {
@@ -543,13 +535,13 @@ bool Object::internalPut(StringOrSymbol *name, const Value &value)
memberIndex.set(engine, value);
}
return true;
- } else if (!prototype()) {
+ } else if (!getPrototypeOf()) {
if (!isExtensible())
return false;
} else {
// clause 4
Scope scope(engine);
- memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs);
+ memberIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(name, &attrs);
if (!memberIndex.isNull()) {
if (attrs.isAccessor()) {
if (!memberIndex->as<FunctionObject>())
@@ -608,13 +600,13 @@ bool Object::internalPutIndexed(uint index, const Value &value)
arrayIndex.set(engine, value);
return true;
- } else if (!prototype()) {
+ } else if (!getPrototypeOf()) {
if (!isExtensible())
return false;
} else {
// clause 4
Scope scope(engine);
- arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs);
+ arrayIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(index, &attrs);
if (!arrayIndex.isNull()) {
if (attrs.isAccessor()) {
if (!arrayIndex->as<FunctionObject>())
@@ -978,7 +970,7 @@ bool Object::hasProperty(const Managed *m, Identifier id)
if (o->getOwnProperty(id, p) != Attr_Invalid)
return true;
- o = o->prototype();
+ o = o->getPrototypeOf();
}
return false;
@@ -1033,6 +1025,34 @@ bool Object::preventExtensions(Managed *m)
return true;
}
+Heap::Object *Object::getPrototypeOf(const Managed *m)
+{
+ return m->internalClass()->prototype;
+}
+
+bool Object::setPrototypeOf(Managed *m, const Object *proto)
+{
+ Q_ASSERT(m->isObject());
+ Object *o = static_cast<Object *>(m);
+ Heap::Object *current = o->internalClass()->prototype;
+ Heap::Object *protod = proto ? proto->d() : nullptr;
+ if (current == protod)
+ return true;
+ if (!o->internalClass()->extensible)
+ return false;
+ Heap::Object *p = protod;
+ while (p) {
+ if (p == o->d())
+ return false;
+ if (reinterpret_cast<const ObjectVTable *>(p->vtable())->getPrototypeOf !=
+ reinterpret_cast<const ObjectVTable *>(Object::staticVTable())->getPrototypeOf)
+ break;
+ p = p->prototype();
+ }
+ o->setInternalClass(o->internalClass()->changePrototype(protod));
+ return true;
+}
+
bool Object::setArrayLength(uint newLen)
{
Q_ASSERT(isArrayObject());
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index b55e8f25e2..6ec7452ca6 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -179,6 +179,8 @@ struct ObjectVTable
PropertyAttributes (*getOwnProperty)(Managed *m, Identifier id, Property *p);
bool (*isExtensible)(const Managed *);
bool (*preventExtensions)(Managed *);
+ Heap::Object *(*getPrototypeOf)(const Managed *);
+ bool (*setPrototypeOf)(Managed *, const Object *);
qint64 (*getLength)(const Managed *m);
void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var);
@@ -200,6 +202,8 @@ const QV4::ObjectVTable classname::static_vtbl = \
getOwnProperty, \
isExtensible, \
preventExtensions, \
+ getPrototypeOf, \
+ setPrototypeOf, \
getLength, \
advanceIterator, \
instanceOf \
@@ -244,8 +248,6 @@ struct Q_QML_EXPORT Object: Managed {
void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); }
const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); }
- Heap::Object *prototype() const { return d()->prototype(); }
- bool setPrototype(Object *proto);
PropertyAttributes getOwnProperty(Identifier id, Property *p = nullptr) {
return vtable()->getOwnProperty(this, id, p);
@@ -307,6 +309,9 @@ struct Q_QML_EXPORT Object: Managed {
bool isExtensible() const { return vtable()->isExtensible(this); }
bool preventExtensions() { return vtable()->preventExtensions(this); }
+ Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); }
+ bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); }
+ void setPrototypeUnchecked(const Object *p);
// Array handling
@@ -366,7 +371,7 @@ public:
Scope scope(engine());
ScopedObject p(scope, this);
- while ((p = p->prototype()))
+ while ((p = p->getPrototypeOf()))
if (p->arrayData())
return true;
@@ -445,6 +450,8 @@ protected:
static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p);
static bool isExtensible(const Managed *m);
static bool preventExtensions(Managed *);
+ static Heap::Object *getPrototypeOf(const Managed *);
+ static bool setPrototypeOf(Managed *, const Object *);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static qint64 getLength(const Managed *m);
static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 109e189ede..fc4a06747f 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -109,7 +109,7 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib
shadowed = true;
break;
}
- o = o->prototype();
+ o = o->getPrototypeOf();
}
if (shadowed)
continue;
@@ -118,7 +118,7 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib
}
if (flags & WithProtoChain)
- current->setM(co->prototype());
+ current->setM(co->getPrototypeOf());
else
current->setM(nullptr);
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 84238477fd..9d52ab1980 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -71,7 +71,7 @@ ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value
ScopedObject obj(scope, scope.engine->newObject());
ScopedObject proto(scope, ctor->get(scope.engine->id_prototype()));
if (!!proto)
- obj->setPrototype(proto);
+ obj->setPrototypeOf(proto);
return obj.asReturnedValue();
} else {
return argv[0].toObject(v4)->asReturnedValue();
@@ -136,7 +136,7 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedObject p(scope, o->prototype());
+ ScopedObject p(scope, o->getPrototypeOf());
return (!!p ? p->asReturnedValue() : Encode::null());
}
@@ -264,7 +264,7 @@ ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, cons
ScopedObject O(scope, argv[0]);
ScopedObject newObject(scope, scope.engine->newObject());
- newObject->setPrototype(O);
+ newObject->setPrototypeOf(O);
if (argc > 1 && !argv[1].isUndefined()) {
@@ -516,17 +516,10 @@ ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, co
return argv[0].asReturnedValue();
ScopedObject o(scope, argv[0]);
- ScopedObject p(scope, argv[1]);
- Q_ASSERT(!!o);
-
- if (o->prototype() != p->d()) {
- bool ok = false;
- if (o->isExtensible()) {
- ok = o->setPrototype(p);
- }
- if (!ok)
- return scope.engine->throwTypeError(QStringLiteral("Object.setPrototypeOf: Could not change prototype"));
- }
+ const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
+ bool ok = o->setPrototypeOf(p);
+ if (!ok)
+ return scope.engine->throwTypeError(QStringLiteral("Could not change prototype."));
return o->asReturnedValue();
}
@@ -604,11 +597,11 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con
ScopedObject O(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedObject proto(scope, V->prototype());
+ ScopedObject proto(scope, V->getPrototypeOf());
while (proto) {
if (O->d() == proto->d())
return Encode(true);
- proto = proto->prototype();
+ proto = proto->getPrototypeOf();
}
return Encode(false);
}
@@ -694,32 +687,21 @@ ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const V
if (!o)
THROW_TYPE_ERROR();
- return Encode(o->prototype());
+ return Encode(o->getPrototypeOf());
}
ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
ScopedObject o(scope, thisObject);
- if (!o || !argc)
+ if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull()))
THROW_TYPE_ERROR();
- if (argv[0].isNull()) {
- o->setPrototype(nullptr);
- RETURN_UNDEFINED();
- }
-
- ScopedObject p(scope, argv[0]);
- bool ok = false;
- if (!!p) {
- if (o->prototype() == p->d()) {
- ok = true;
- } else if (o->isExtensible()) {
- ok = o->setPrototype(p);
- }
- }
+ const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv);
+ bool ok = o->setPrototypeOf(p);
if (!ok)
- return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
+ return scope.engine->throwTypeError(QStringLiteral("Could not change prototype."));
+ return Encode::undefined();
RETURN_UNDEFINED();
}
diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp
index e499dfbcf1..80e1b70a1a 100644
--- a/src/qml/jsruntime/qv4proxy.cpp
+++ b/src/qml/jsruntime/qv4proxy.cpp
@@ -361,6 +361,89 @@ bool ProxyObject::preventExtensions(Managed *m)
return result;
}
+Heap::Object *ProxyObject::getPrototypeOf(const Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf")));
+ ScopedValue trap(scope, handler->get(name));
+ if (scope.hasException())
+ return nullptr;
+ if (trap->isNullOrUndefined())
+ return target->getPrototypeOf();
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->isNull() && !trapResult->isObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject());
+ if (!target->isExtensible()) {
+ Heap::Object *targetProto = target->getPrototypeOf();
+ if (proto != targetProto) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ }
+ return proto;
+}
+
+bool ProxyObject::setPrototypeOf(Managed *m, const Object *p)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf")));
+ ScopedValue trap(scope, handler->get(name));
+ if (scope.hasException())
+ return false;
+ if (trap->isNullOrUndefined())
+ return target->setPrototypeOf(p);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = p ? p->asReturnedValue() : Encode::null();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result)
+ return false;
+ if (!target->isExtensible()) {
+ Heap::Object *targetProto = target->getPrototypeOf();
+ if (p->d() != targetProto) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ }
+ return true;
+}
+
//ReturnedValue ProxyObject::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
//{
diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h
index a8b5b63a6c..cbabc7e5d9 100644
--- a/src/qml/jsruntime/qv4proxy_p.h
+++ b/src/qml/jsruntime/qv4proxy_p.h
@@ -95,6 +95,8 @@ struct ProxyObject: Object {
static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p);
static bool isExtensible(const Managed *m);
static bool preventExtensions(Managed *);
+ static Heap::Object *getPrototypeOf(const Managed *);
+ static bool setPrototypeOf(Managed *, const Object *);
// those might require a second proxy object that derives from FunctionObject...
// static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 2ab77502d6..94d645ac2b 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -2093,7 +2093,7 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
}
Scoped<QMetaObjectWrapper> metaObject(scope, this);
object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototype(const_cast<QMetaObjectWrapper*>(this));
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
return object.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
index 219e28b6e7..9b4a5a2375 100644
--- a/src/qml/jsruntime/qv4reflect.cpp
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -175,12 +175,14 @@ ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f,
return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc);
}
-ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
if (!argc || !argv[0].isObject())
return f->engine()->throwTypeError();
- return ObjectPrototype::method_getPrototypeOf(f, thisObject, argv, argc);
+ const Object *o = static_cast<const Object *>(argv);
+ Heap::Object *p = o->getPrototypeOf();
+ return (p ? p->asReturnedValue() : Encode::null());
}
ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc)
@@ -260,10 +262,13 @@ ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const
return Encode(result);
}
-ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- if (argc < 2 || !argv[0].isObject())
+ if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject()))
return f->engine()->throwTypeError();
- return ObjectPrototype::method_setPrototypeOf(f, thisObject, argv, argc);
+ Scope scope(f);
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
+ return Encode(o->setPrototypeOf(proto));
}
diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h
index 73d257e006..d480e1d914 100644
--- a/src/qml/jsruntime/qv4reflect_p.h
+++ b/src/qml/jsruntime/qv4reflect_p.h
@@ -73,7 +73,7 @@ struct Reflect : Object {
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_getPrototypeOf(const FunctionObject *, const Value *, 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);
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index 3eadfe04a6..86aa18c3a4 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -410,9 +410,9 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3));
ctor->defineReadonlyProperty(engine->id_prototype(), *this);
ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
- ctor->setPrototype(engine->intrinsicTypedArrayCtor());
+ ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor());
- setPrototype(engine->intrinsicTypedArrayPrototype());
+ setPrototypeOf(engine->intrinsicTypedArrayPrototype());
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
}
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 22e20de8d7..9986d3577a 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -1409,7 +1409,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode));
QV4::ScopedObject p(scope, e->incubationProto.value());
- r->setPrototype(p);
+ r->setPrototypeOf(p);
if (!valuemap->isUndefined())
r->d()->valuemap.set(scope.engine, valuemap);
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index f17f0fb77a..cdca4f794c 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -828,7 +828,7 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale)
QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>());
*wrapper->d()->locale = locale;
QV4::ScopedObject p(scope, d->prototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
return wrapper.asReturnedValue();
}
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 41531c1df3..a8faac4b0d 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -611,7 +611,7 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
switch (data->type) {
case NodeImpl::Attr:
- instance->setPrototype((p = Attr::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Attr::prototype(v4)));
break;
case NodeImpl::Comment:
case NodeImpl::Document:
@@ -623,13 +623,13 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
case NodeImpl::ProcessingInstruction:
return Encode::undefined();
case NodeImpl::CDATA:
- instance->setPrototype((p = CDATA::prototype(v4)));
+ instance->setPrototypeUnchecked((p = CDATA::prototype(v4)));
break;
case NodeImpl::Text:
- instance->setPrototype((p = Text::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Text::prototype(v4)));
break;
case NodeImpl::Element:
- instance->setPrototype((p = Element::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Element::prototype(v4)));
break;
}
@@ -643,7 +643,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine)
Scope scope(engine);
ScopedObject p(scope, engine->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(engine)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr);
d->elementPrototype.set(engine, p);
engine->v8Engine->freezeObject(p);
@@ -658,7 +658,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine)
Scope scope(engine);
ScopedObject p(scope, engine->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(engine)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("name"), method_name, nullptr);
p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr);
p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr);
@@ -715,7 +715,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(v4)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr);
p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr);
d->characterDataPrototype.set(v4, p);
@@ -751,7 +751,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = CharacterData::prototype(v4)));
+ p->setPrototypeUnchecked((pp = CharacterData::prototype(v4)));
p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr);
p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr);
d->textPrototype.set(v4, p);
@@ -768,7 +768,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = Text::prototype(v4)));
+ p->setPrototypeUnchecked((pp = Text::prototype(v4)));
d->cdataPrototype.set(v4, p);
v4->v8Engine->freezeObject(p);
}
@@ -782,7 +782,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(v4)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
p->defineAccessorProperty(QStringLiteral("xmlVersion"), method_xmlVersion, nullptr);
p->defineAccessorProperty(QStringLiteral("xmlEncoding"), method_xmlEncoding, nullptr);
p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr);
@@ -879,7 +879,7 @@ ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data)
ScopedObject instance(scope, v4->memoryManager->allocate<Node>(document));
document->release(); // the GC should own the NodeImpl via Node now
ScopedObject p(scope);
- instance->setPrototype((p = Document::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Document::prototype(v4)));
return instance.asReturnedValue();
}
@@ -1650,7 +1650,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
- w->setPrototype(proto);
+ w->setPrototypeUnchecked(proto);
return w.asReturnedValue();
}
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
index b5e25fb6d4..f99c4def45 100644
--- a/src/qml/qml/v8/qv8engine.cpp
+++ b/src/qml/qml/v8/qv8engine.cpp
@@ -223,13 +223,13 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
QV4::Scope scope(v4);
bool instanceOfObject = false;
- QV4::ScopedObject p(scope, object->prototype());
+ QV4::ScopedObject p(scope, object->getPrototypeOf());
while (p) {
if (p->d() == v4->objectPrototype()->d()) {
instanceOfObject = true;
break;
}
- p = p->prototype();
+ p = p->getPrototypeOf();
}
if (!instanceOfObject)
return;
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 33fe5f3438..98b90af5c3 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -2563,7 +2563,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
QV4::Scope scope(v4);
QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem));
QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value());
- o->setPrototype(p);
+ o->setPrototypeOf(p);
++cacheItem->scriptRef;
return QQmlV4Handle(o);
@@ -3395,7 +3395,7 @@ public:
QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value());
QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4));
- object->setPrototype(changeProto);
+ object->setPrototypeOf(changeProto);
object->d()->change = change;
if (hasProperty)
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index 3016316e57..4130eff33a 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -439,7 +439,7 @@ public:
QV4::Scope scope(v4);
QV4::ScopedObject proto(scope, type->prototype.value());
QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
- o->setPrototype(proto);
+ o->setPrototypeOf(proto);
++scriptRef;
return o.asReturnedValue();
}
@@ -620,7 +620,7 @@ public:
QV4::Scope scope(v4);
QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
QV4::ScopedObject p(scope, data->listItemProto.value());
- o->setPrototype(p);
+ o->setPrototypeOf(p);
++scriptRef;
return o.asReturnedValue();
}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 07d5f6d39b..314b7904b4 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -956,7 +956,7 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
QQuickContext2DEngineData *ed = engineData(scope.engine);
QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DPixelData>());
QV4::ScopedObject p(scope, ed->pixelArrayProto.value());
- pixelData->setPrototype(p);
+ pixelData->setPrototypeOf(p);
if (image.isNull()) {
*pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
@@ -1584,7 +1584,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
RETURN_RESULT(*gradient);
}
@@ -1636,7 +1636,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r1, QPointF(x0, y0), r0);
RETURN_RESULT(*gradient);
}
@@ -1680,7 +1680,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(cons
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QConicalGradient(x, y, angle);
RETURN_RESULT(*gradient);
}
@@ -4401,7 +4401,7 @@ void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
QV4::Scope scope(engine);
QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
QV4::ScopedObject p(scope, ed->contextPrototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
wrapper->d()->context = this;
m_v4value = wrapper;
}