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 : };
26 :
27 1 : obj1 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
28 1 : CHECK(obj1);
29 1 : obj2 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
30 1 : CHECK(obj2);
31 1 : JS_SetPrivate(obj1, this);
32 1 : JS_SetPrivate(obj2, this);
33 :
34 1 : CHECK(JS_DefineProperty(cx, global, "obj1", OBJECT_TO_JSVAL(obj1), NULL, NULL, 0));
35 1 : CHECK(JS_DefineProperty(cx, global, "obj2", OBJECT_TO_JSVAL(obj2), NULL, NULL, 0));
36 :
37 1 : resolveEntryCount = 0;
38 1 : resolveExitCount = 0;
39 :
40 : /* Start the essence of the test via invoking the first resolve hook. */
41 : jsval v;
42 1 : EVAL("obj1.x", &v);
43 1 : CHECK_SAME(v, JSVAL_FALSE);
44 1 : CHECK_EQUAL(resolveEntryCount, 4);
45 1 : CHECK_EQUAL(resolveExitCount, 4);
46 1 : return true;
47 : }
48 :
49 : JSObject *obj1;
50 : JSObject *obj2;
51 : unsigned resolveEntryCount;
52 : unsigned resolveExitCount;
53 :
54 : struct AutoIncrCounters {
55 :
56 4 : AutoIncrCounters(cls_testResolveRecursion *t) : t(t) {
57 4 : t->resolveEntryCount++;
58 4 : }
59 :
60 4 : ~AutoIncrCounters() {
61 4 : t->resolveExitCount++;
62 4 : }
63 :
64 : cls_testResolveRecursion *t;
65 : };
66 :
67 : bool
68 4 : doResolve(JSObject *obj, jsid id, unsigned flags, JSObject **objp)
69 : {
70 4 : CHECK_EQUAL(resolveExitCount, 0);
71 8 : AutoIncrCounters incr(this);
72 4 : CHECK_EQUAL(obj, obj1 || obj == obj2);
73 :
74 4 : CHECK(JSID_IS_STRING(id));
75 :
76 4 : JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
77 4 : CHECK(str);
78 : jsval v;
79 4 : if (JS_FlatStringEqualsAscii(str, "x")) {
80 2 : if (obj == obj1) {
81 : /* First resolve hook invocation. */
82 1 : CHECK_EQUAL(resolveEntryCount, 1);
83 1 : EVAL("obj2.y = true", &v);
84 1 : CHECK_SAME(v, JSVAL_TRUE);
85 1 : CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, NULL, NULL, 0));
86 1 : *objp = obj;
87 1 : return true;
88 : }
89 1 : if (obj == obj2) {
90 1 : CHECK_EQUAL(resolveEntryCount, 4);
91 1 : *objp = NULL;
92 1 : return true;
93 : }
94 2 : } else if (JS_FlatStringEqualsAscii(str, "y")) {
95 2 : if (obj == obj2) {
96 1 : CHECK_EQUAL(resolveEntryCount, 2);
97 1 : CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, NULL, NULL, 0));
98 1 : EVAL("obj1.x", &v);
99 1 : CHECK(JSVAL_IS_VOID(v));
100 1 : EVAL("obj1.y", &v);
101 1 : CHECK_SAME(v, JSVAL_ZERO);
102 1 : *objp = obj;
103 1 : return true;
104 : }
105 1 : if (obj == obj1) {
106 1 : CHECK_EQUAL(resolveEntryCount, 3);
107 1 : EVAL("obj1.x", &v);
108 1 : CHECK(JSVAL_IS_VOID(v));
109 1 : EVAL("obj1.y", &v);
110 1 : CHECK(JSVAL_IS_VOID(v));
111 1 : EVAL("obj2.y", &v);
112 1 : CHECK(JSVAL_IS_NULL(v));
113 1 : EVAL("obj2.x", &v);
114 1 : CHECK(JSVAL_IS_VOID(v));
115 1 : EVAL("obj1.y = 0", &v);
116 1 : CHECK_SAME(v, JSVAL_ZERO);
117 1 : *objp = obj;
118 1 : return true;
119 : }
120 : }
121 0 : CHECK(false);
122 : return false;
123 : }
124 :
125 : static JSBool
126 4 : my_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
127 : {
128 4 : return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(obj))->
129 4 : doResolve(obj, id, flags, objp);
130 : }
131 :
132 2 : END_TEST(testResolveRecursion)
|