1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : */
4 :
5 : #include "tests.h"
6 :
7 : /*
8 : * Test that resolve hook recursion for the same object and property is
9 : * prevented.
10 : */
11 :
12 4 : BEGIN_TEST(testResolveRecursion)
13 : {
14 : static JSClass my_resolve_class = {
15 : "MyResolve",
16 : JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE,
17 :
18 : JS_PropertyStub, // add
19 : JS_PropertyStub, // delete
20 : JS_PropertyStub, // get
21 : JS_StrictPropertyStub, // set
22 : JS_EnumerateStub,
23 : (JSResolveOp) my_resolve,
24 : JS_ConvertStub,
25 : JS_FinalizeStub,
26 : JSCLASS_NO_OPTIONAL_MEMBERS
27 : };
28 :
29 1 : obj1 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
30 1 : CHECK(obj1);
31 1 : obj2 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
32 1 : CHECK(obj2);
33 1 : JS_SetPrivate(obj1, this);
34 1 : JS_SetPrivate(obj2, this);
35 :
36 1 : CHECK(JS_DefineProperty(cx, global, "obj1", OBJECT_TO_JSVAL(obj1), NULL, NULL, 0));
37 1 : CHECK(JS_DefineProperty(cx, global, "obj2", OBJECT_TO_JSVAL(obj2), NULL, NULL, 0));
38 :
39 1 : resolveEntryCount = 0;
40 1 : resolveExitCount = 0;
41 :
42 : /* Start the essence of the test via invoking the first resolve hook. */
43 : jsval v;
44 1 : EVAL("obj1.x", &v);
45 1 : CHECK_SAME(v, JSVAL_FALSE);
46 1 : CHECK_EQUAL(resolveEntryCount, 4);
47 1 : CHECK_EQUAL(resolveExitCount, 4);
48 1 : return true;
49 : }
50 :
51 : JSObject *obj1;
52 : JSObject *obj2;
53 : unsigned resolveEntryCount;
54 : unsigned resolveExitCount;
55 :
56 : struct AutoIncrCounters {
57 :
58 4 : AutoIncrCounters(cls_testResolveRecursion *t) : t(t) {
59 4 : t->resolveEntryCount++;
60 4 : }
61 :
62 4 : ~AutoIncrCounters() {
63 4 : t->resolveExitCount++;
64 4 : }
65 :
66 : cls_testResolveRecursion *t;
67 : };
68 :
69 : bool
70 4 : doResolve(JSObject *obj, jsid id, unsigned flags, JSObject **objp)
71 : {
72 4 : CHECK_EQUAL(resolveExitCount, 0);
73 8 : AutoIncrCounters incr(this);
74 4 : CHECK_EQUAL(obj, obj1 || obj == obj2);
75 :
76 4 : CHECK(JSID_IS_STRING(id));
77 :
78 4 : JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
79 4 : CHECK(str);
80 : jsval v;
81 4 : if (JS_FlatStringEqualsAscii(str, "x")) {
82 2 : if (obj == obj1) {
83 : /* First resolve hook invocation. */
84 1 : CHECK_EQUAL(resolveEntryCount, 1);
85 1 : EVAL("obj2.y = true", &v);
86 1 : CHECK_SAME(v, JSVAL_TRUE);
87 1 : CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, NULL, NULL, 0));
88 1 : *objp = obj;
89 1 : return true;
90 : }
91 1 : if (obj == obj2) {
92 1 : CHECK_EQUAL(resolveEntryCount, 4);
93 1 : *objp = NULL;
94 1 : return true;
95 : }
96 2 : } else if (JS_FlatStringEqualsAscii(str, "y")) {
97 2 : if (obj == obj2) {
98 1 : CHECK_EQUAL(resolveEntryCount, 2);
99 1 : CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, NULL, NULL, 0));
100 1 : EVAL("obj1.x", &v);
101 1 : CHECK(JSVAL_IS_VOID(v));
102 1 : EVAL("obj1.y", &v);
103 1 : CHECK_SAME(v, JSVAL_ZERO);
104 1 : *objp = obj;
105 1 : return true;
106 : }
107 1 : if (obj == obj1) {
108 1 : CHECK_EQUAL(resolveEntryCount, 3);
109 1 : EVAL("obj1.x", &v);
110 1 : CHECK(JSVAL_IS_VOID(v));
111 1 : EVAL("obj1.y", &v);
112 1 : CHECK(JSVAL_IS_VOID(v));
113 1 : EVAL("obj2.y", &v);
114 1 : CHECK(JSVAL_IS_NULL(v));
115 1 : EVAL("obj2.x", &v);
116 1 : CHECK(JSVAL_IS_VOID(v));
117 1 : EVAL("obj1.y = 0", &v);
118 1 : CHECK_SAME(v, JSVAL_ZERO);
119 1 : *objp = obj;
120 1 : return true;
121 : }
122 : }
123 0 : CHECK(false);
124 : return false;
125 : }
126 :
127 : static JSBool
128 4 : my_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
129 : {
130 4 : return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(obj))->
131 4 : doResolve(obj, id, flags, objp);
132 : }
133 :
134 2 : END_TEST(testResolveRecursion)
|