1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsapi-tests/testResolveRecursion.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,146 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "jsapi-tests/tests.h" 1.12 + 1.13 +/* 1.14 + * Test that resolve hook recursion for the same object and property is 1.15 + * prevented. 1.16 + */ 1.17 + 1.18 +BEGIN_TEST(testResolveRecursion) 1.19 +{ 1.20 + static const JSClass my_resolve_class = { 1.21 + "MyResolve", 1.22 + JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE, 1.23 + 1.24 + JS_PropertyStub, // add 1.25 + JS_DeletePropertyStub, // delete 1.26 + JS_PropertyStub, // get 1.27 + JS_StrictPropertyStub, // set 1.28 + JS_EnumerateStub, 1.29 + (JSResolveOp) my_resolve, 1.30 + JS_ConvertStub 1.31 + }; 1.32 + 1.33 + obj1 = obj2 = nullptr; 1.34 + JS::AddObjectRoot(cx, &obj1); 1.35 + JS::AddObjectRoot(cx, &obj2); 1.36 + 1.37 + obj1 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr()); 1.38 + CHECK(obj1); 1.39 + obj2 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr()); 1.40 + CHECK(obj2); 1.41 + JS_SetPrivate(obj1, this); 1.42 + JS_SetPrivate(obj2, this); 1.43 + 1.44 + JS::RootedValue obj1Val(cx, ObjectValue(*obj1)); 1.45 + JS::RootedValue obj2Val(cx, ObjectValue(*obj2)); 1.46 + CHECK(JS_DefineProperty(cx, global, "obj1", obj1Val, 0)); 1.47 + CHECK(JS_DefineProperty(cx, global, "obj2", obj2Val, 0)); 1.48 + 1.49 + resolveEntryCount = 0; 1.50 + resolveExitCount = 0; 1.51 + 1.52 + /* Start the essence of the test via invoking the first resolve hook. */ 1.53 + JS::RootedValue v(cx); 1.54 + EVAL("obj1.x", &v); 1.55 + CHECK_SAME(v, JSVAL_FALSE); 1.56 + CHECK_EQUAL(resolveEntryCount, 4); 1.57 + CHECK_EQUAL(resolveExitCount, 4); 1.58 + 1.59 + obj1 = nullptr; 1.60 + obj2 = nullptr; 1.61 + JS::RemoveObjectRoot(cx, &obj1); 1.62 + JS::RemoveObjectRoot(cx, &obj2); 1.63 + return true; 1.64 +} 1.65 + 1.66 +JS::Heap<JSObject *> obj1; 1.67 +JS::Heap<JSObject *> obj2; 1.68 +unsigned resolveEntryCount; 1.69 +unsigned resolveExitCount; 1.70 + 1.71 +struct AutoIncrCounters { 1.72 + 1.73 + AutoIncrCounters(cls_testResolveRecursion *t) : t(t) { 1.74 + t->resolveEntryCount++; 1.75 + } 1.76 + 1.77 + ~AutoIncrCounters() { 1.78 + t->resolveExitCount++; 1.79 + } 1.80 + 1.81 + cls_testResolveRecursion *t; 1.82 +}; 1.83 + 1.84 +bool 1.85 +doResolve(JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp) 1.86 +{ 1.87 + CHECK_EQUAL(resolveExitCount, 0); 1.88 + AutoIncrCounters incr(this); 1.89 + CHECK_EQUAL(obj, obj1 || obj == obj2); 1.90 + 1.91 + CHECK(JSID_IS_STRING(id)); 1.92 + 1.93 + JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id)); 1.94 + CHECK(str); 1.95 + JS::RootedValue v(cx); 1.96 + if (JS_FlatStringEqualsAscii(str, "x")) { 1.97 + if (obj == obj1) { 1.98 + /* First resolve hook invocation. */ 1.99 + CHECK_EQUAL(resolveEntryCount, 1); 1.100 + EVAL("obj2.y = true", &v); 1.101 + CHECK_SAME(v, JSVAL_TRUE); 1.102 + CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, nullptr, nullptr, 0)); 1.103 + objp.set(obj); 1.104 + return true; 1.105 + } 1.106 + if (obj == obj2) { 1.107 + CHECK_EQUAL(resolveEntryCount, 4); 1.108 + objp.set(nullptr); 1.109 + return true; 1.110 + } 1.111 + } else if (JS_FlatStringEqualsAscii(str, "y")) { 1.112 + if (obj == obj2) { 1.113 + CHECK_EQUAL(resolveEntryCount, 2); 1.114 + CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, nullptr, nullptr, 0)); 1.115 + EVAL("obj1.x", &v); 1.116 + CHECK(JSVAL_IS_VOID(v)); 1.117 + EVAL("obj1.y", &v); 1.118 + CHECK_SAME(v, JSVAL_ZERO); 1.119 + objp.set(obj); 1.120 + return true; 1.121 + } 1.122 + if (obj == obj1) { 1.123 + CHECK_EQUAL(resolveEntryCount, 3); 1.124 + EVAL("obj1.x", &v); 1.125 + CHECK(JSVAL_IS_VOID(v)); 1.126 + EVAL("obj1.y", &v); 1.127 + CHECK(JSVAL_IS_VOID(v)); 1.128 + EVAL("obj2.y", &v); 1.129 + CHECK(JSVAL_IS_NULL(v)); 1.130 + EVAL("obj2.x", &v); 1.131 + CHECK(JSVAL_IS_VOID(v)); 1.132 + EVAL("obj1.y = 0", &v); 1.133 + CHECK_SAME(v, JSVAL_ZERO); 1.134 + objp.set(obj); 1.135 + return true; 1.136 + } 1.137 + } 1.138 + CHECK(false); 1.139 + return false; 1.140 +} 1.141 + 1.142 +static bool 1.143 +my_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp) 1.144 +{ 1.145 + return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(obj))-> 1.146 + doResolve(obj, id, objp); 1.147 +} 1.148 + 1.149 +END_TEST(testResolveRecursion)