Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "jsapi-tests/tests.h"
10 /*
11 * Test that resolve hook recursion for the same object and property is
12 * prevented.
13 */
15 BEGIN_TEST(testResolveRecursion)
16 {
17 static const JSClass my_resolve_class = {
18 "MyResolve",
19 JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE,
21 JS_PropertyStub, // add
22 JS_DeletePropertyStub, // delete
23 JS_PropertyStub, // get
24 JS_StrictPropertyStub, // set
25 JS_EnumerateStub,
26 (JSResolveOp) my_resolve,
27 JS_ConvertStub
28 };
30 obj1 = obj2 = nullptr;
31 JS::AddObjectRoot(cx, &obj1);
32 JS::AddObjectRoot(cx, &obj2);
34 obj1 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
35 CHECK(obj1);
36 obj2 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
37 CHECK(obj2);
38 JS_SetPrivate(obj1, this);
39 JS_SetPrivate(obj2, this);
41 JS::RootedValue obj1Val(cx, ObjectValue(*obj1));
42 JS::RootedValue obj2Val(cx, ObjectValue(*obj2));
43 CHECK(JS_DefineProperty(cx, global, "obj1", obj1Val, 0));
44 CHECK(JS_DefineProperty(cx, global, "obj2", obj2Val, 0));
46 resolveEntryCount = 0;
47 resolveExitCount = 0;
49 /* Start the essence of the test via invoking the first resolve hook. */
50 JS::RootedValue v(cx);
51 EVAL("obj1.x", &v);
52 CHECK_SAME(v, JSVAL_FALSE);
53 CHECK_EQUAL(resolveEntryCount, 4);
54 CHECK_EQUAL(resolveExitCount, 4);
56 obj1 = nullptr;
57 obj2 = nullptr;
58 JS::RemoveObjectRoot(cx, &obj1);
59 JS::RemoveObjectRoot(cx, &obj2);
60 return true;
61 }
63 JS::Heap<JSObject *> obj1;
64 JS::Heap<JSObject *> obj2;
65 unsigned resolveEntryCount;
66 unsigned resolveExitCount;
68 struct AutoIncrCounters {
70 AutoIncrCounters(cls_testResolveRecursion *t) : t(t) {
71 t->resolveEntryCount++;
72 }
74 ~AutoIncrCounters() {
75 t->resolveExitCount++;
76 }
78 cls_testResolveRecursion *t;
79 };
81 bool
82 doResolve(JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
83 {
84 CHECK_EQUAL(resolveExitCount, 0);
85 AutoIncrCounters incr(this);
86 CHECK_EQUAL(obj, obj1 || obj == obj2);
88 CHECK(JSID_IS_STRING(id));
90 JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
91 CHECK(str);
92 JS::RootedValue v(cx);
93 if (JS_FlatStringEqualsAscii(str, "x")) {
94 if (obj == obj1) {
95 /* First resolve hook invocation. */
96 CHECK_EQUAL(resolveEntryCount, 1);
97 EVAL("obj2.y = true", &v);
98 CHECK_SAME(v, JSVAL_TRUE);
99 CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, nullptr, nullptr, 0));
100 objp.set(obj);
101 return true;
102 }
103 if (obj == obj2) {
104 CHECK_EQUAL(resolveEntryCount, 4);
105 objp.set(nullptr);
106 return true;
107 }
108 } else if (JS_FlatStringEqualsAscii(str, "y")) {
109 if (obj == obj2) {
110 CHECK_EQUAL(resolveEntryCount, 2);
111 CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, nullptr, nullptr, 0));
112 EVAL("obj1.x", &v);
113 CHECK(JSVAL_IS_VOID(v));
114 EVAL("obj1.y", &v);
115 CHECK_SAME(v, JSVAL_ZERO);
116 objp.set(obj);
117 return true;
118 }
119 if (obj == obj1) {
120 CHECK_EQUAL(resolveEntryCount, 3);
121 EVAL("obj1.x", &v);
122 CHECK(JSVAL_IS_VOID(v));
123 EVAL("obj1.y", &v);
124 CHECK(JSVAL_IS_VOID(v));
125 EVAL("obj2.y", &v);
126 CHECK(JSVAL_IS_NULL(v));
127 EVAL("obj2.x", &v);
128 CHECK(JSVAL_IS_VOID(v));
129 EVAL("obj1.y = 0", &v);
130 CHECK_SAME(v, JSVAL_ZERO);
131 objp.set(obj);
132 return true;
133 }
134 }
135 CHECK(false);
136 return false;
137 }
139 static bool
140 my_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
141 {
142 return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(obj))->
143 doResolve(obj, id, objp);
144 }
146 END_TEST(testResolveRecursion)