tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testResolveRecursion.cpp (5914B)


      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/. */
      7 
      8 #include "js/Object.h"              // JS::GetReservedSlot, JS::SetReservedSlot
      9 #include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_DefinePropertyById
     10 #include "jsapi-tests/tests.h"
     11 
     12 /*
     13 * Test that resolve hook recursion for the same object and property is
     14 * prevented.
     15 */
     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
     28  };
     29 
     30  static const JSClass my_resolve_class = {
     31      "MyResolve",
     32      JSCLASS_HAS_RESERVED_SLOTS(SlotCount),
     33      &my_resolve_classOps,
     34  };
     35 
     36  obj1.init(cx, JS_NewObject(cx, &my_resolve_class));
     37  CHECK(obj1);
     38  obj2.init(cx, JS_NewObject(cx, &my_resolve_class));
     39  CHECK(obj2);
     40  JS::SetReservedSlot(obj1, TestSlot, JS::PrivateValue(this));
     41  JS::SetReservedSlot(obj2, TestSlot, JS::PrivateValue(this));
     42 
     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));
     47 
     48  resolveEntryCount = 0;
     49  resolveExitCount = 0;
     50 
     51  /* Start the essence of the test via invoking the first resolve hook. */
     52  JS::RootedValue v(cx);
     53  EVAL("obj1.x", &v);
     54  CHECK(v.isFalse());
     55  CHECK_EQUAL(resolveEntryCount, 4);
     56  CHECK_EQUAL(resolveExitCount, 4);
     57 
     58  obj1 = nullptr;
     59  obj2 = nullptr;
     60  return true;
     61 }
     62 
     63 enum Slots { TestSlot, SlotCount };
     64 
     65 JS::PersistentRootedObject obj1;
     66 JS::PersistentRootedObject obj2;
     67 int resolveEntryCount;
     68 int resolveExitCount;
     69 
     70 struct AutoIncrCounters {
     71  explicit AutoIncrCounters(cls_testResolveRecursion* t) : t(t) {
     72    t->resolveEntryCount++;
     73  }
     74 
     75  ~AutoIncrCounters() { t->resolveExitCount++; }
     76 
     77  cls_testResolveRecursion* t;
     78 };
     79 
     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);
     84 
     85  CHECK(id.isString());
     86 
     87  JSLinearString* str = JS_EnsureLinearString(cx, id.toString());
     88  CHECK(str);
     89  JS::RootedValue v(cx);
     90  if (JS_LinearStringEqualsLiteral(str, "x")) {
     91    if (obj == obj1) {
     92      /* First resolve hook invocation. */
     93      CHECK_EQUAL(resolveEntryCount, 1);
     94      EVAL("obj2.y = true", &v);
     95      CHECK(v.isTrue());
     96      CHECK(JS_DefinePropertyById(cx, obj, id, JS::FalseHandleValue,
     97                                  JSPROP_RESOLVING));
     98      *resolvedp = true;
     99      return true;
    100    }
    101    if (obj == obj2) {
    102      CHECK_EQUAL(resolveEntryCount, 4);
    103      *resolvedp = false;
    104      return true;
    105    }
    106  } else if (JS_LinearStringEqualsLiteral(str, "y")) {
    107    if (obj == obj2) {
    108      CHECK_EQUAL(resolveEntryCount, 2);
    109      CHECK(JS_DefinePropertyById(cx, obj, id, JS::NullHandleValue,
    110                                  JSPROP_RESOLVING));
    111      EVAL("obj1.x", &v);
    112      CHECK(v.isUndefined());
    113      EVAL("obj1.y", &v);
    114      CHECK(v.isInt32(0));
    115      *resolvedp = true;
    116      return true;
    117    }
    118    if (obj == obj1) {
    119      CHECK_EQUAL(resolveEntryCount, 3);
    120      EVAL("obj1.x", &v);
    121      CHECK(v.isUndefined());
    122      EVAL("obj1.y", &v);
    123      CHECK(v.isUndefined());
    124      EVAL("obj2.y", &v);
    125      CHECK(v.isNull());
    126      EVAL("obj2.x", &v);
    127      CHECK(v.isUndefined());
    128      EVAL("obj1.y = 0", &v);
    129      CHECK(v.isInt32(0));
    130      *resolvedp = true;
    131      return true;
    132    }
    133  }
    134  CHECK(false);
    135  return false;
    136 }
    137 
    138 static bool my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
    139                       bool* resolvedp) {
    140  void* p = JS::GetReservedSlot(obj, TestSlot).toPrivate();
    141  return static_cast<cls_testResolveRecursion*>(p)->doResolve(obj, id,
    142                                                              resolvedp);
    143 }
    144 END_TEST(testResolveRecursion)
    145 
    146 /*
    147 * Test that JS_InitStandardClasses does not cause resolve hooks to be called.
    148 *
    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.)
    153 */
    154 BEGIN_TEST(testResolveRecursion_InitStandardClasses) {
    155  CHECK(JS::InitRealmStandardClasses(cx));
    156  return true;
    157 }
    158 
    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
    167      nullptr,                   // finalize
    168      nullptr,                   // call
    169      nullptr,                   // construct
    170      JS_GlobalObjectTraceHook,  // trace
    171  };
    172 
    173  static const JSClass myGlobalClass = {
    174      "testResolveRecursion_InitStandardClasses_myGlobalClass",
    175      JSCLASS_GLOBAL_FLAGS,
    176      &myGlobalClassOps,
    177  };
    178 
    179  return &myGlobalClass;
    180 }
    181 
    182 static bool my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
    183                       bool* resolvedp) {
    184  MOZ_ASSERT_UNREACHABLE(
    185      "resolve hook should not be called from InitStandardClasses");
    186  JS_ReportErrorASCII(cx, "FAIL");
    187  return false;
    188 }
    189 END_TEST(testResolveRecursion_InitStandardClasses)