js/src/jsapi-tests/testResolveRecursion.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jsapi-tests/testResolveRecursion.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,146 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + */
     1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    1.10 +
    1.11 +#include "jsapi-tests/tests.h"
    1.12 +
    1.13 +/*
    1.14 + * Test that resolve hook recursion for the same object and property is
    1.15 + * prevented.
    1.16 + */
    1.17 +
    1.18 +BEGIN_TEST(testResolveRecursion)
    1.19 +{
    1.20 +    static const JSClass my_resolve_class = {
    1.21 +        "MyResolve",
    1.22 +        JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE,
    1.23 +
    1.24 +        JS_PropertyStub,       // add
    1.25 +        JS_DeletePropertyStub, // delete
    1.26 +        JS_PropertyStub,       // get
    1.27 +        JS_StrictPropertyStub, // set
    1.28 +        JS_EnumerateStub,
    1.29 +        (JSResolveOp) my_resolve,
    1.30 +        JS_ConvertStub
    1.31 +    };
    1.32 +
    1.33 +    obj1 = obj2 = nullptr;
    1.34 +    JS::AddObjectRoot(cx, &obj1);
    1.35 +    JS::AddObjectRoot(cx, &obj2);
    1.36 +
    1.37 +    obj1 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
    1.38 +    CHECK(obj1);
    1.39 +    obj2 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
    1.40 +    CHECK(obj2);
    1.41 +    JS_SetPrivate(obj1, this);
    1.42 +    JS_SetPrivate(obj2, this);
    1.43 +
    1.44 +    JS::RootedValue obj1Val(cx, ObjectValue(*obj1));
    1.45 +    JS::RootedValue obj2Val(cx, ObjectValue(*obj2));
    1.46 +    CHECK(JS_DefineProperty(cx, global, "obj1", obj1Val, 0));
    1.47 +    CHECK(JS_DefineProperty(cx, global, "obj2", obj2Val, 0));
    1.48 +
    1.49 +    resolveEntryCount = 0;
    1.50 +    resolveExitCount = 0;
    1.51 +
    1.52 +    /* Start the essence of the test via invoking the first resolve hook. */
    1.53 +    JS::RootedValue v(cx);
    1.54 +    EVAL("obj1.x", &v);
    1.55 +    CHECK_SAME(v, JSVAL_FALSE);
    1.56 +    CHECK_EQUAL(resolveEntryCount, 4);
    1.57 +    CHECK_EQUAL(resolveExitCount, 4);
    1.58 +
    1.59 +    obj1 = nullptr;
    1.60 +    obj2 = nullptr;
    1.61 +    JS::RemoveObjectRoot(cx, &obj1);
    1.62 +    JS::RemoveObjectRoot(cx, &obj2);
    1.63 +    return true;
    1.64 +}
    1.65 +
    1.66 +JS::Heap<JSObject *> obj1;
    1.67 +JS::Heap<JSObject *> obj2;
    1.68 +unsigned resolveEntryCount;
    1.69 +unsigned resolveExitCount;
    1.70 +
    1.71 +struct AutoIncrCounters {
    1.72 +
    1.73 +    AutoIncrCounters(cls_testResolveRecursion *t) : t(t) {
    1.74 +        t->resolveEntryCount++;
    1.75 +    }
    1.76 +
    1.77 +    ~AutoIncrCounters() {
    1.78 +        t->resolveExitCount++;
    1.79 +    }
    1.80 +
    1.81 +    cls_testResolveRecursion *t;
    1.82 +};
    1.83 +
    1.84 +bool
    1.85 +doResolve(JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
    1.86 +{
    1.87 +    CHECK_EQUAL(resolveExitCount, 0);
    1.88 +    AutoIncrCounters incr(this);
    1.89 +    CHECK_EQUAL(obj, obj1 || obj == obj2);
    1.90 +
    1.91 +    CHECK(JSID_IS_STRING(id));
    1.92 +
    1.93 +    JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
    1.94 +    CHECK(str);
    1.95 +    JS::RootedValue v(cx);
    1.96 +    if (JS_FlatStringEqualsAscii(str, "x")) {
    1.97 +        if (obj == obj1) {
    1.98 +            /* First resolve hook invocation. */
    1.99 +            CHECK_EQUAL(resolveEntryCount, 1);
   1.100 +            EVAL("obj2.y = true", &v);
   1.101 +            CHECK_SAME(v, JSVAL_TRUE);
   1.102 +            CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, nullptr, nullptr, 0));
   1.103 +            objp.set(obj);
   1.104 +            return true;
   1.105 +        }
   1.106 +        if (obj == obj2) {
   1.107 +            CHECK_EQUAL(resolveEntryCount, 4);
   1.108 +            objp.set(nullptr);
   1.109 +            return true;
   1.110 +        }
   1.111 +    } else if (JS_FlatStringEqualsAscii(str, "y")) {
   1.112 +        if (obj == obj2) {
   1.113 +            CHECK_EQUAL(resolveEntryCount, 2);
   1.114 +            CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, nullptr, nullptr, 0));
   1.115 +            EVAL("obj1.x", &v);
   1.116 +            CHECK(JSVAL_IS_VOID(v));
   1.117 +            EVAL("obj1.y", &v);
   1.118 +            CHECK_SAME(v, JSVAL_ZERO);
   1.119 +            objp.set(obj);
   1.120 +            return true;
   1.121 +        }
   1.122 +        if (obj == obj1) {
   1.123 +            CHECK_EQUAL(resolveEntryCount, 3);
   1.124 +            EVAL("obj1.x", &v);
   1.125 +            CHECK(JSVAL_IS_VOID(v));
   1.126 +            EVAL("obj1.y", &v);
   1.127 +            CHECK(JSVAL_IS_VOID(v));
   1.128 +            EVAL("obj2.y", &v);
   1.129 +            CHECK(JSVAL_IS_NULL(v));
   1.130 +            EVAL("obj2.x", &v);
   1.131 +            CHECK(JSVAL_IS_VOID(v));
   1.132 +            EVAL("obj1.y = 0", &v);
   1.133 +            CHECK_SAME(v, JSVAL_ZERO);
   1.134 +            objp.set(obj);
   1.135 +            return true;
   1.136 +        }
   1.137 +    }
   1.138 +    CHECK(false);
   1.139 +    return false;
   1.140 +}
   1.141 +
   1.142 +static bool
   1.143 +my_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
   1.144 +{
   1.145 +    return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(obj))->
   1.146 +           doResolve(obj, id, objp);
   1.147 +}
   1.148 +
   1.149 +END_TEST(testResolveRecursion)

mercurial