Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/jsapi-tests/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 12 kB image not shown  

Quelle  testObjectSwap.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/*
 * Test JSObject::swap.
 *
 * This test creates objects from a description of their configuration. Objects
 * are given unique property names and values. A list of configurations is
 * created and the result of swapping every combination checked.
 */


#include "mozilla/Sprintf.h"

#include "js/AllocPolicy.h"
#include "js/Vector.h"
#include "jsapi-tests/tests.h"
#include "vm/PlainObject.h"

#include "gc/StableCellHasher-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"

using namespace js;

struct NativeConfig {
  uint32_t propCount;
  uint32_t elementCount;
  bool inDictionaryMode;
};

struct ProxyConfig {
  bool inlineValues;
};

struct ObjectConfig {
  const JSClass* clasp;
  bool isNative;
  bool nurseryAllocated;
  bool hasUniqueId;
  union {
    NativeConfig native;
    ProxyConfig proxy;
  };
};

using ObjectConfigVector = Vector<ObjectConfig, 0, SystemAllocPolicy>;

static const JSClass TestProxyClasses[] = {
    PROXY_CLASS_DEF("TestProxy", JSCLASS_HAS_RESERVED_SLOTS(1 /* Min */)),
    PROXY_CLASS_DEF("TestProxy", JSCLASS_HAS_RESERVED_SLOTS(2)),
    PROXY_CLASS_DEF("TestProxy", JSCLASS_HAS_RESERVED_SLOTS(7)),
    PROXY_CLASS_DEF("TestProxy", JSCLASS_HAS_RESERVED_SLOTS(8)),
    PROXY_CLASS_DEF("TestProxy", JSCLASS_HAS_RESERVED_SLOTS(14 /* Max */)),
};

static const JSClass TestDOMClasses[] = {
    {"TestDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(0)},
    {"TestDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1)},
    {"TestDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2)},
    {"TestDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(7)},
    {"TestDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(8)},
    {"TestDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(20)},
};

static const uint32_t TestPropertyCounts[] = {0, 1, 2, 7, 8, 20};

static const uint32_t TestElementCounts[] = {0, 20};

static bool Verbose = false;

class TenuredProxyHandler final : public Wrapper {
 public:
  static const TenuredProxyHandler singleton;
  constexpr TenuredProxyHandler() : Wrapper(0) {}
  bool canNurseryAllocate() const override { return false; }
};

const TenuredProxyHandler TenuredProxyHandler::singleton;

class NurseryProxyHandler final : public Wrapper {
 public:
  static const NurseryProxyHandler singleton;
  constexpr NurseryProxyHandler() : Wrapper(0) {}
  bool canNurseryAllocate() const override { return true; }
};

const NurseryProxyHandler NurseryProxyHandler::singleton;

BEGIN_TEST(testObjectSwap) {
  AutoLeaveZeal noZeal(cx);

  ObjectConfigVector objectConfigs = CreateObjectConfigs();

  for (const ObjectConfig& config1 : objectConfigs) {
    for (const ObjectConfig& config2 : objectConfigs) {
      {
        uint32_t id1;
        RootedObject obj1(cx, CreateObject(config1, &id1));
        CHECK(obj1);

        uint32_t id2;
        RootedObject obj2(cx, CreateObject(config2, &id2));
        CHECK(obj2);

        if (Verbose) {
          fprintf(stderr, "Swap %p (%s) and %p (%s)\n", obj1.get(),
                  GetLocation(obj1), obj2.get(), GetLocation(obj2));
        }

        uint64_t uid1 = 0;
        if (config1.hasUniqueId) {
          uid1 = gc::GetUniqueIdInfallible(obj1);
        }
        uint64_t uid2 = 0;
        if (config2.hasUniqueId) {
          uid2 = gc::GetUniqueIdInfallible(obj2);
        }

        {
          AutoEnterOOMUnsafeRegion oomUnsafe;
          JSObject::swap(cx, obj1, obj2, oomUnsafe);
        }

        CHECK(CheckObject(obj1, config2, id2));
        CHECK(CheckObject(obj2, config1, id1));

        CHECK(CheckUniqueIds(obj1, config1.hasUniqueId, uid1, obj2,
                             config2.hasUniqueId, uid2));

        // Check we can promote swapped nursery objects.
        cx->minorGC(JS::GCReason::API);
      }

      if (Verbose) {
        fprintf(stderr, "\n");
      }
    }

    // JSObject::swap can suppress GC so ensure we clean up occasionally.
    JS_GC(cx);
  }

  return true;
}

ObjectConfigVector CreateObjectConfigs() {
  ObjectConfigVector configs;

  ObjectConfig config;

  for (bool nurseryAllocated : {falsetrue}) {
    config.nurseryAllocated = nurseryAllocated;

    for (bool hasUniqueId : {falsetrue}) {
      config.hasUniqueId = hasUniqueId;

      config.isNative = true;
      config.native = NativeConfig{0, false};

      for (const JSClass& jsClass : TestDOMClasses) {
        config.clasp = &jsClass;

        for (uint32_t propCount : TestPropertyCounts) {
          config.native.propCount = propCount;

          for (uint32_t elementCount : TestElementCounts) {
            config.native.elementCount = elementCount;

            for (bool inDictionaryMode : {falsetrue}) {
              if (inDictionaryMode && propCount == 0) {
                continue;
              }

              config.native.inDictionaryMode = inDictionaryMode;
              MOZ_RELEASE_ASSERT(configs.append(config));
            }
          }
        }
      }

      config.isNative = false;
      config.proxy = ProxyConfig{false};

      for (const JSClass& jsClass : TestProxyClasses) {
        config.clasp = &jsClass;

        for (bool inlineValues : {truefalse}) {
          config.proxy.inlineValues = inlineValues;
          MOZ_RELEASE_ASSERT(configs.append(config));
        }
      }
    }
  }

  return configs;
}

const char* GetLocation(JSObject* obj) {
  return obj->isTenured() ? "tenured heap" : "nursery";
}

// Counter used to give slots and property names unique values.
uint32_t nextId = 0;

JSObject* CreateObject(const ObjectConfig& config, uint32_t* idOut) {
  *idOut = nextId;
  JSObject* obj =
      config.isNative ? CreateNativeObject(config) : CreateProxy(config);

  if (config.hasUniqueId) {
    uint64_t unused;
    if (!gc::GetOrCreateUniqueId(obj, &unused)) {
      return nullptr;
    }
  }

  return obj;
}

JSObject* CreateNativeObject(const ObjectConfig& config) {
  MOZ_ASSERT(config.isNative);

  NewObjectKind kind = config.nurseryAllocated ? GenericObject : TenuredObject;
  Rooted<NativeObject*> obj(cx,
                            NewBuiltinClassInstance(cx, config.clasp, kind));
  if (!obj) {
    return nullptr;
  }

  MOZ_RELEASE_ASSERT(IsInsideNursery(obj) == config.nurseryAllocated);

  for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(config.clasp); i++) {
    JS::SetReservedSlot(obj, i, Int32Value(nextId++));
  }

  if (config.native.inDictionaryMode) {
    // Put object in dictionary mode by defining a non-last property and
    // deleting it later.
    MOZ_RELEASE_ASSERT(config.native.propCount != 0);
    if (!JS_DefineProperty(cx, obj, "dummy", 0, JSPROP_ENUMERATE)) {
      return nullptr;
    }
  }

  for (uint32_t i = 0; i < config.native.propCount; i++) {
    RootedId name(cx, CreatePropName(nextId++));
    if (name.isVoid()) {
      return nullptr;
    }

    if (!JS_DefinePropertyById(cx, obj, name, nextId++, JSPROP_ENUMERATE)) {
      return nullptr;
    }
  }

  if (config.native.elementCount) {
    if (!obj->ensureElements(cx, config.native.elementCount)) {
      return nullptr;
    }
    for (uint32_t i = 0; i < config.native.elementCount; i++) {
      obj->setDenseInitializedLength(i + 1);
      obj->initDenseElement(i, Int32Value(nextId++));
    }
    MOZ_ASSERT(obj->hasDynamicElements());
  }

  if (config.native.inDictionaryMode) {
    JS::ObjectOpResult result;
    JS_DeleteProperty(cx, obj, "dummy", result);
    MOZ_RELEASE_ASSERT(result.ok());
  }

  MOZ_RELEASE_ASSERT(obj->inDictionaryMode() == config.native.inDictionaryMode);

  return obj;
}

JSObject* CreateProxy(const ObjectConfig& config) {
  RootedValue priv(cx, Int32Value(nextId++));

  RootedObject expando(cx, NewPlainObject(cx));
  RootedValue expandoId(cx, Int32Value(nextId++));
  if (!expando || !JS_SetProperty(cx, expando, "id", expandoId)) {
    return nullptr;
  }

  ProxyOptions options;
  options.setClass(config.clasp);
  options.setLazyProto(true);

  const Wrapper* handler;
  if (config.nurseryAllocated) {
    handler = &NurseryProxyHandler::singleton;
  } else {
    handler = &TenuredProxyHandler::singleton;
  }

  RootedObject obj(cx, NewProxyObject(cx, handler, priv, nullptr, options));
  if (!obj) {
    return nullptr;
  }

  Rooted<ProxyObject*> proxy(cx, &obj->as<ProxyObject>());
  proxy->setExpando(expando);

  for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(config.clasp); i++) {
    JS::SetReservedSlot(proxy, i, Int32Value(nextId++));
  }

  if (!config.proxy.inlineValues) {
    // To create a proxy with non-inline values we must swap the proxy with an
    // object with a different size.
    NewObjectKind kind =
        config.nurseryAllocated ? GenericObject : TenuredObject;
    RootedObject dummy(cx,
                       NewBuiltinClassInstance(cx, &TestDOMClasses[0], kind));
    if (!dummy) {
      return nullptr;
    }

    AutoEnterOOMUnsafeRegion oomUnsafe;
    JSObject::swap(cx, obj, dummy, oomUnsafe);
    proxy = &dummy->as<ProxyObject>();
  }

  MOZ_RELEASE_ASSERT(IsInsideNursery(proxy) == config.nurseryAllocated);
  MOZ_RELEASE_ASSERT(proxy->usingInlineValueArray() ==
                     config.proxy.inlineValues);

  return proxy;
}

bool CheckObject(HandleObject obj, const ObjectConfig& config, uint32_t id) {
  CHECK(obj->is<NativeObject>() == config.isNative);
  CHECK(obj->getClass() == config.clasp);

  uint32_t reservedSlots = JSCLASS_RESERVED_SLOTS(config.clasp);

  if (Verbose) {
    fprintf(stderr, "Check %p is a %s object with %u reserved slots", obj.get(),
            config.isNative ? "native" : "proxy", reservedSlots);
    if (config.isNative) {
      fprintf(stderr,
              ", %u properties, %u elements and %s in dictionary mode\n",
              config.native.propCount, config.native.elementCount,
              config.native.inDictionaryMode ? "is" : "is not");
    } else {
      fprintf(stderr, " with %s values\n",
              config.proxy.inlineValues ? "inline" : "out-of-line");
    }
  }

  if (!config.isNative) {
    CHECK(GetProxyPrivate(obj) == Int32Value(id++));

    Value expandoValue = GetProxyExpando(obj);
    CHECK(expandoValue.isObject());

    RootedObject expando(cx, &expandoValue.toObject());
    RootedValue expandoId(cx);
    JS_GetProperty(cx, expando, "id", &expandoId);
    CHECK(expandoId == Int32Value(id++));
  }

  for (uint32_t i = 0; i < reservedSlots; i++) {
    CHECK(JS::GetReservedSlot(obj, i) == Int32Value(id++));
  }

  if (config.isNative) {
    Rooted<NativeObject*> nobj(cx, &obj->as<NativeObject>());
    uint32_t propCount = GetPropertyCount(nobj);

    CHECK(propCount == config.native.propCount);

    for (uint32_t i = 0; i < config.native.propCount; i++) {
      RootedId name(cx, CreatePropName(id++));
      CHECK(!name.isVoid());

      RootedValue value(cx);
      CHECK(JS_GetPropertyById(cx, obj, name, &value));
      CHECK(value == Int32Value(id++));
    }

    CHECK(nobj->getDenseInitializedLength() == config.native.elementCount);
    for (uint32_t i = 0; i < config.native.elementCount; i++) {
      Value value = nobj->getDenseElement(i);
      CHECK(value == Int32Value(id++));
    }
  }

  return true;
}

bool CheckUniqueIds(HandleObject obj1, bool hasUniqueId1, uint64_t uid1,
                    HandleObject obj2, bool hasUniqueId2, uint64_t uid2) {
  if (uid1 && uid2) {
    MOZ_RELEASE_ASSERT(uid1 != uid2);
  }

  // Check unique IDs are NOT swapped.
  CHECK(CheckUniqueId(obj1, hasUniqueId1, uid1));
  CHECK(CheckUniqueId(obj2, hasUniqueId2, uid2));

  // Check unique IDs are different if present.
  if (gc::HasUniqueId(obj1) && gc::HasUniqueId(obj2)) {
    CHECK(gc::GetUniqueIdInfallible(obj1) != gc::GetUniqueIdInfallible(obj2));
  }

  return true;
}

bool CheckUniqueId(HandleObject obj, bool hasUniqueId, uint64_t uid) {
  if (hasUniqueId) {
    CHECK(gc::HasUniqueId(obj));
    CHECK(gc::GetUniqueIdInfallible(obj) == uid);
  } else {
    // Swap may add a unique ID to an object.
  }

  if (obj->is<NativeObject>()) {
    CHECK(!obj->zone()->uniqueIds().has(obj));
  }
  return true;
}

uint32_t GetPropertyCount(NativeObject* obj) {
  uint32_t count = 0;
  for (ShapePropertyIter<NoGC> iter(obj->shape()); !iter.done(); iter++) {
    count++;
  }

  return count;
}

jsid CreatePropName(uint32_t id) {
  char buffer[32];
  SprintfLiteral(buffer, "prop%u", id);

  RootedString atom(cx, JS_AtomizeString(cx, buffer));
  if (!atom) {
    return jsid::Void();
  }

  return jsid::NonIntAtom(atom);
}

END_TEST(testObjectSwap)

77%


¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.