Backed out changeset 8f976ed899d7 (bug 1847231) for causing bc failures on browser_se...
[gecko.git] / js / src / jsapi-tests / testResolveRecursion.cpp
blob44b5cd35a329fce01f57dc2d1eb3d5764cc5f8cc
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:
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 "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
14 * prevented.
16 BEGIN_TEST(testResolveRecursion) {
17 static const JSClassOps my_resolve_classOps = {
18 nullptr, // addProperty
19 nullptr, // delProperty
20 nullptr, // enumerate
21 nullptr, // newEnumerate
22 my_resolve, // resolve
23 nullptr, // mayResolve
24 nullptr, // finalize
25 nullptr, // call
26 nullptr, // construct
27 nullptr, // trace
30 static const JSClass my_resolve_class = {
31 "MyResolve", JSCLASS_HAS_RESERVED_SLOTS(SlotCount), &my_resolve_classOps};
33 obj1.init(cx, JS_NewObject(cx, &my_resolve_class));
34 CHECK(obj1);
35 obj2.init(cx, JS_NewObject(cx, &my_resolve_class));
36 CHECK(obj2);
37 JS::SetReservedSlot(obj1, TestSlot, JS::PrivateValue(this));
38 JS::SetReservedSlot(obj2, TestSlot, JS::PrivateValue(this));
40 JS::RootedValue obj1Val(cx, JS::ObjectValue(*obj1));
41 JS::RootedValue obj2Val(cx, JS::ObjectValue(*obj2));
42 CHECK(JS_DefineProperty(cx, global, "obj1", obj1Val, 0));
43 CHECK(JS_DefineProperty(cx, global, "obj2", obj2Val, 0));
45 resolveEntryCount = 0;
46 resolveExitCount = 0;
48 /* Start the essence of the test via invoking the first resolve hook. */
49 JS::RootedValue v(cx);
50 EVAL("obj1.x", &v);
51 CHECK(v.isFalse());
52 CHECK_EQUAL(resolveEntryCount, 4);
53 CHECK_EQUAL(resolveExitCount, 4);
55 obj1 = nullptr;
56 obj2 = nullptr;
57 return true;
60 enum Slots { TestSlot, SlotCount };
62 JS::PersistentRootedObject obj1;
63 JS::PersistentRootedObject obj2;
64 int resolveEntryCount;
65 int resolveExitCount;
67 struct AutoIncrCounters {
68 explicit AutoIncrCounters(cls_testResolveRecursion* t) : t(t) {
69 t->resolveEntryCount++;
72 ~AutoIncrCounters() { t->resolveExitCount++; }
74 cls_testResolveRecursion* t;
77 bool doResolve(JS::HandleObject obj, JS::HandleId id, bool* resolvedp) {
78 CHECK_EQUAL(resolveExitCount, 0);
79 AutoIncrCounters incr(this);
80 CHECK(obj == obj1 || obj == obj2);
82 CHECK(id.isString());
84 JSLinearString* str = JS_EnsureLinearString(cx, id.toString());
85 CHECK(str);
86 JS::RootedValue v(cx);
87 if (JS_LinearStringEqualsLiteral(str, "x")) {
88 if (obj == obj1) {
89 /* First resolve hook invocation. */
90 CHECK_EQUAL(resolveEntryCount, 1);
91 EVAL("obj2.y = true", &v);
92 CHECK(v.isTrue());
93 CHECK(JS_DefinePropertyById(cx, obj, id, JS::FalseHandleValue,
94 JSPROP_RESOLVING));
95 *resolvedp = true;
96 return true;
98 if (obj == obj2) {
99 CHECK_EQUAL(resolveEntryCount, 4);
100 *resolvedp = false;
101 return true;
103 } else if (JS_LinearStringEqualsLiteral(str, "y")) {
104 if (obj == obj2) {
105 CHECK_EQUAL(resolveEntryCount, 2);
106 CHECK(JS_DefinePropertyById(cx, obj, id, JS::NullHandleValue,
107 JSPROP_RESOLVING));
108 EVAL("obj1.x", &v);
109 CHECK(v.isUndefined());
110 EVAL("obj1.y", &v);
111 CHECK(v.isInt32(0));
112 *resolvedp = true;
113 return true;
115 if (obj == obj1) {
116 CHECK_EQUAL(resolveEntryCount, 3);
117 EVAL("obj1.x", &v);
118 CHECK(v.isUndefined());
119 EVAL("obj1.y", &v);
120 CHECK(v.isUndefined());
121 EVAL("obj2.y", &v);
122 CHECK(v.isNull());
123 EVAL("obj2.x", &v);
124 CHECK(v.isUndefined());
125 EVAL("obj1.y = 0", &v);
126 CHECK(v.isInt32(0));
127 *resolvedp = true;
128 return true;
131 CHECK(false);
132 return false;
135 static bool my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
136 bool* resolvedp) {
137 void* p = JS::GetReservedSlot(obj, TestSlot).toPrivate();
138 return static_cast<cls_testResolveRecursion*>(p)->doResolve(obj, id,
139 resolvedp);
141 END_TEST(testResolveRecursion)
144 * Test that JS_InitStandardClasses does not cause resolve hooks to be called.
146 * (XPConnect apparently does have global classes, such as the one created by
147 * nsMessageManagerScriptExecutor::InitChildGlobalInternal(), that have resolve
148 * hooks which can call back into JS, and on which JS_InitStandardClasses is
149 * called. Calling back into JS in the middle of resolving `undefined` is bad.)
151 BEGIN_TEST(testResolveRecursion_InitStandardClasses) {
152 CHECK(JS::InitRealmStandardClasses(cx));
153 return true;
156 const JSClass* getGlobalClass() override {
157 static const JSClassOps myGlobalClassOps = {
158 nullptr, // addProperty
159 nullptr, // delProperty
160 nullptr, // enumerate
161 nullptr, // newEnumerate
162 my_resolve, // resolve
163 nullptr, // mayResolve
164 nullptr, // finalize
165 nullptr, // call
166 nullptr, // construct
167 JS_GlobalObjectTraceHook, // trace
170 static const JSClass myGlobalClass = {
171 "testResolveRecursion_InitStandardClasses_myGlobalClass",
172 JSCLASS_GLOBAL_FLAGS, &myGlobalClassOps};
174 return &myGlobalClass;
177 static bool my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
178 bool* resolvedp) {
179 MOZ_ASSERT_UNREACHABLE(
180 "resolve hook should not be called from InitStandardClasses");
181 JS_ReportErrorASCII(cx, "FAIL");
182 return false;
184 END_TEST(testResolveRecursion_InitStandardClasses)