Properly implement Object.prototype.__proto__
authorLars Knoll <lars.knoll@digia.com>
Thu, 13 Jun 2013 12:11:23 +0000 (14:11 +0200)
committerSimon Hausmann <simon.hausmann@digia.com>
Thu, 13 Jun 2013 12:15:45 +0000 (14:15 +0200)
Change-Id: I5c3247f46cd86020425f6df8674f8bda7410757b
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/qml/v4/qv4object.cpp
src/qml/qml/v4/qv4objectproto.cpp
src/qml/qml/v4/qv4objectproto_p.h
tests/auto/qml/qjsvalue/tst_qjsvalue.cpp

index 7394c1f..637f229 100644 (file)
@@ -587,12 +587,6 @@ Value Object::internalGet(ExecutionContext *ctx, String *name, bool *hasProperty
 
     name->makeIdentifier(ctx);
 
-    if (name->isEqualTo(ctx->engine->id___proto__)) {
-        if (hasProperty)
-            *hasProperty = true;
-        return Value::fromObject(prototype);
-    }
-
     Object *o = this;
     while (o) {
         uint idx = o->internalClass->find(name);
index efb40ee..0a69588 100644 (file)
@@ -126,6 +126,11 @@ void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor)
     defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1);
     defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0);
     defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0);
+
+    ExecutionEngine *v4 = ctx->engine;
+    Property *p = insertMember(v4->id___proto__, Attr_Accessor|Attr_NotEnumerable);
+    p->setGetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_get_proto));
+    p->setSetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_set_proto));
 }
 
 Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx)
@@ -456,6 +461,47 @@ Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx)
     return Value::undefinedValue();
 }
 
+Value ObjectPrototype::method_get_proto(SimpleCallContext *ctx)
+{
+    Object *o = ctx->thisObject.asObject();
+    if (!o)
+        ctx->throwTypeError();
+
+    return Value::fromObject(o->prototype);
+}
+
+Value ObjectPrototype::method_set_proto(SimpleCallContext *ctx)
+{
+    Object *o = ctx->thisObject.asObject();
+    if (!o)
+        ctx->throwTypeError();
+
+    Value proto = ctx->argument(0);
+    bool ok = false;
+    if (proto.isNull()) {
+        o->prototype = 0;
+        ok = true;
+    } else if (Object *p = proto.asObject()) {
+        if (o->prototype == p) {
+            ok = true;
+        } else if (o->extensible) {
+            Object *pp = p;
+            while (pp) {
+                if (pp == o)
+                    break;
+                pp = pp->prototype;
+            }
+            if (!pp) {
+                ok = true;
+                o->prototype = p;
+            }
+        }
+    }
+    if (!ok)
+        ctx->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
+    return Value::undefinedValue();
+}
+
 void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs)
 {
     if (!v.isObject())
index e266953..84bc715 100644 (file)
@@ -90,6 +90,9 @@ struct ObjectPrototype: Object
     static Value method_defineGetter(SimpleCallContext *ctx);
     static Value method_defineSetter(SimpleCallContext *ctx);
 
+    static Value method_get_proto(SimpleCallContext *ctx);
+    static Value method_set_proto(SimpleCallContext *ctx);
+
     static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs);
     static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs);
 
index 8d13541..2b8da26 100644 (file)
@@ -1584,7 +1584,7 @@ void tst_QJSValue::getSetPrototype_evalCyclicPrototype()
     QJSEngine eng;
     QJSValue ret = eng.evaluate("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o");
     QCOMPARE(ret.isError(), true);
-    QCOMPARE(ret.toString(), QLatin1String("Error: Cyclic __proto__ value"));
+    QCOMPARE(ret.toString(), QLatin1String("TypeError: Cyclic __proto__ value"));
 }
 
 void tst_QJSValue::getSetPrototype_eval()