1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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 "js/Object.h" // JS::GetReservedSlot, JS::SetReservedSlot
9 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById
10 #include "jsapi-tests/tests.h"
13 * Test that resolve hook recursion for the same object and property is
16 BEGIN_TEST(testResolveRecursion
) {
17 static const JSClassOps my_resolve_classOps
= {
18 nullptr, // addProperty
19 nullptr, // delProperty
21 nullptr, // newEnumerate
22 my_resolve
, // resolve
23 nullptr, // mayResolve
30 static const JSClass my_resolve_class
= {
32 JSCLASS_HAS_RESERVED_SLOTS(SlotCount
),
36 obj1
.init(cx
, JS_NewObject(cx
, &my_resolve_class
));
38 obj2
.init(cx
, JS_NewObject(cx
, &my_resolve_class
));
40 JS::SetReservedSlot(obj1
, TestSlot
, JS::PrivateValue(this));
41 JS::SetReservedSlot(obj2
, TestSlot
, JS::PrivateValue(this));
43 JS::RootedValue
obj1Val(cx
, JS::ObjectValue(*obj1
));
44 JS::RootedValue
obj2Val(cx
, JS::ObjectValue(*obj2
));
45 CHECK(JS_DefineProperty(cx
, global
, "obj1", obj1Val
, 0));
46 CHECK(JS_DefineProperty(cx
, global
, "obj2", obj2Val
, 0));
48 resolveEntryCount
= 0;
51 /* Start the essence of the test via invoking the first resolve hook. */
52 JS::RootedValue
v(cx
);
55 CHECK_EQUAL(resolveEntryCount
, 4);
56 CHECK_EQUAL(resolveExitCount
, 4);
63 enum Slots
{ TestSlot
, SlotCount
};
65 JS::PersistentRootedObject obj1
;
66 JS::PersistentRootedObject obj2
;
67 int resolveEntryCount
;
70 struct AutoIncrCounters
{
71 explicit AutoIncrCounters(cls_testResolveRecursion
* t
) : t(t
) {
72 t
->resolveEntryCount
++;
75 ~AutoIncrCounters() { t
->resolveExitCount
++; }
77 cls_testResolveRecursion
* t
;
80 bool doResolve(JS::HandleObject obj
, JS::HandleId id
, bool* resolvedp
) {
81 CHECK_EQUAL(resolveExitCount
, 0);
82 AutoIncrCounters
incr(this);
83 CHECK(obj
== obj1
|| obj
== obj2
);
87 JSLinearString
* str
= JS_EnsureLinearString(cx
, id
.toString());
89 JS::RootedValue
v(cx
);
90 if (JS_LinearStringEqualsLiteral(str
, "x")) {
92 /* First resolve hook invocation. */
93 CHECK_EQUAL(resolveEntryCount
, 1);
94 EVAL("obj2.y = true", &v
);
96 CHECK(JS_DefinePropertyById(cx
, obj
, id
, JS::FalseHandleValue
,
102 CHECK_EQUAL(resolveEntryCount
, 4);
106 } else if (JS_LinearStringEqualsLiteral(str
, "y")) {
108 CHECK_EQUAL(resolveEntryCount
, 2);
109 CHECK(JS_DefinePropertyById(cx
, obj
, id
, JS::NullHandleValue
,
112 CHECK(v
.isUndefined());
119 CHECK_EQUAL(resolveEntryCount
, 3);
121 CHECK(v
.isUndefined());
123 CHECK(v
.isUndefined());
127 CHECK(v
.isUndefined());
128 EVAL("obj1.y = 0", &v
);
138 static bool my_resolve(JSContext
* cx
, JS::HandleObject obj
, JS::HandleId id
,
140 void* p
= JS::GetReservedSlot(obj
, TestSlot
).toPrivate();
141 return static_cast<cls_testResolveRecursion
*>(p
)->doResolve(obj
, id
,
144 END_TEST(testResolveRecursion
)
147 * Test that JS_InitStandardClasses does not cause resolve hooks to be called.
149 * (XPConnect apparently does have global classes, such as the one created by
150 * nsMessageManagerScriptExecutor::InitChildGlobalInternal(), that have resolve
151 * hooks which can call back into JS, and on which JS_InitStandardClasses is
152 * called. Calling back into JS in the middle of resolving `undefined` is bad.)
154 BEGIN_TEST(testResolveRecursion_InitStandardClasses
) {
155 CHECK(JS::InitRealmStandardClasses(cx
));
159 const JSClass
* getGlobalClass() override
{
160 static const JSClassOps myGlobalClassOps
= {
161 nullptr, // addProperty
162 nullptr, // delProperty
163 nullptr, // enumerate
164 nullptr, // newEnumerate
165 my_resolve
, // resolve
166 nullptr, // mayResolve
169 nullptr, // construct
170 JS_GlobalObjectTraceHook
, // trace
173 static const JSClass myGlobalClass
= {
174 "testResolveRecursion_InitStandardClasses_myGlobalClass",
175 JSCLASS_GLOBAL_FLAGS
,
179 return &myGlobalClass
;
182 static bool my_resolve(JSContext
* cx
, JS::HandleObject obj
, JS::HandleId id
,
184 MOZ_ASSERT_UNREACHABLE(
185 "resolve hook should not be called from InitStandardClasses");
186 JS_ReportErrorASCII(cx
, "FAIL");
189 END_TEST(testResolveRecursion_InitStandardClasses
)