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

Quelle  Watchtower.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/. */


#include "vm/Watchtower.h"

#include "js/CallAndConstruct.h"
#include "vm/Compartment.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/NativeObject.h"
#include "vm/PlainObject.h"
#include "vm/Realm.h"

#include "vm/Compartment-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/Realm-inl.h"
#include "vm/Shape-inl.h"

using namespace js;

static bool AddToWatchtowerLog(JSContext* cx, const char* kind,
                               HandleObject obj, HandleValue extra) {
  // Add an object storing {kind, object, extra} to the log for testing
  // purposes.

  MOZ_ASSERT(obj->useWatchtowerTestingLog());

  RootedString kindString(cx, NewStringCopyZ<CanGC>(cx, kind));
  if (!kindString) {
    return false;
  }

  Rooted<PlainObject*> logObj(cx, NewPlainObjectWithProto(cx, nullptr));
  if (!logObj) {
    return false;
  }
  if (!JS_DefineProperty(cx, logObj, "kind", kindString, JSPROP_ENUMERATE)) {
    return false;
  }
  if (!JS_DefineProperty(cx, logObj, "object", obj, JSPROP_ENUMERATE)) {
    return false;
  }
  if (!JS_DefineProperty(cx, logObj, "extra", extra, JSPROP_ENUMERATE)) {
    return false;
  }

  if (!cx->runtime()->watchtowerTestingLog->append(logObj)) {
    ReportOutOfMemory(cx);
    return false;
  }

  return true;
}

static bool ReshapeForShadowedProp(JSContext* cx, Handle<NativeObject*> obj,
                                   HandleId id) {
  // |obj| has been used as the prototype of another object. Check if we're
  // shadowing a property on its proto chain. In this case we need to reshape
  // that object for shape teleporting to work correctly.
  //
  // See also the 'Shape Teleporting Optimization' comment in jit/CacheIR.cpp.

  MOZ_ASSERT(obj->isUsedAsPrototype());

  // Lookups on integer ids cannot be cached through prototypes.
  if (id.isInt()) {
    return true;
  }

  RootedObject proto(cx, obj->staticPrototype());
  while (proto) {
    // Lookups will not be cached through non-native protos.
    if (!proto->is<NativeObject>()) {
      break;
    }

    if (proto->as<NativeObject>().contains(cx, id)) {
      return JSObject::setInvalidatedTeleporting(cx, proto);
    }

    proto = proto->staticPrototype();
  }

  return true;
}

static void InvalidateMegamorphicCache(JSContext* cx, Handle<NativeObject*> obj,
                                       bool invalidateGetPropCache = true) {
  // The megamorphic cache only checks the receiver object's shape. We need to
  // invalidate the cache when a prototype object changes its set of properties,
  // to account for cached properties that are deleted, turned into an accessor
  // property, or shadowed by another object on the proto chain.

  MOZ_ASSERT(obj->isUsedAsPrototype());

  if (invalidateGetPropCache) {
    cx->caches().megamorphicCache.bumpGeneration();
  }
  cx->caches().megamorphicSetPropCache->bumpGeneration();
}

void MaybePopReturnFuses(JSContext* cx, Handle<NativeObject*> nobj) {
  GlobalObject* global = &nobj->global();
  JSObject* objectProto = &global->getObjectPrototype();
  if (nobj == objectProto) {
    nobj->realm()->realmFuses.objectPrototypeHasNoReturnProperty.popFuse(
        cx, nobj->realm()->realmFuses);
    return;
  }

  JSObject* iteratorProto = global->maybeGetIteratorPrototype();
  if (nobj == iteratorProto) {
    nobj->realm()->realmFuses.iteratorPrototypeHasNoReturnProperty.popFuse(
        cx, nobj->realm()->realmFuses);
    return;
  }

  JSObject* arrayIterProto = global->maybeGetArrayIteratorPrototype();
  if (nobj == arrayIterProto) {
    nobj->realm()->realmFuses.arrayIteratorPrototypeHasNoReturnProperty.popFuse(
        cx, nobj->realm()->realmFuses);
    return;
  }
}

// static
bool Watchtower::watchPropertyAddSlow(JSContext* cx, Handle<NativeObject*> obj,
                                      HandleId id) {
  MOZ_ASSERT(watchesPropertyAdd(obj));

  if (obj->isUsedAsPrototype()) {
    if (!ReshapeForShadowedProp(cx, obj, id)) {
      return false;
    }
    if (!id.isInt()) {
      InvalidateMegamorphicCache(cx, obj);
    }

    if (id == NameToId(cx->names().return_)) {
      MaybePopReturnFuses(cx, obj);
    }
  }

  if (MOZ_UNLIKELY(obj->useWatchtowerTestingLog())) {
    RootedValue val(cx, IdToValue(id));
    if (!AddToWatchtowerLog(cx, "add-prop", obj, val)) {
      return false;
    }
  }

  return true;
}

static bool ReshapeForProtoMutation(JSContext* cx, HandleObject obj) {
  // To avoid the JIT guarding on each prototype in the proto chain to detect
  // prototype mutation, we can instead reshape the rest of the proto chain such
  // that a guard on any of them is sufficient. To avoid excessive reshaping and
  // invalidation, we apply heuristics to decide when to apply this and when
  // to require a guard.
  //
  // There are two cases:
  //
  // (1) The object is not marked IsUsedAsPrototype. This is the common case.
  //     Because shape implies proto, we rely on the caller changing the
  //     object's shape. The JIT guards on this object's shape or prototype so
  //     there's nothing we have to do here for objects on the proto chain.
  //
  // (2) The object is marked IsUsedAsPrototype. This implies the object may be
  //     participating in shape teleporting. To invalidate JIT ICs depending on
  //     the proto chain being unchanged, set the InvalidatedTeleporting shape
  //     flag for this object and objects on its proto chain.
  //
  //     This flag disables future shape teleporting attempts, so next time this
  //     happens the loop below will be a no-op.
  //
  // NOTE: We only handle NativeObjects and don't propagate reshapes through
  //       any non-native objects on the chain.
  //
  // See Also:
  //  - GeneratePrototypeGuards
  //  - GeneratePrototypeHoleGuards

  MOZ_ASSERT(obj->isUsedAsPrototype());

  RootedObject pobj(cx, obj);

  while (pobj && pobj->is<NativeObject>()) {
    if (!pobj->hasInvalidatedTeleporting()) {
      if (!JSObject::setInvalidatedTeleporting(cx, pobj)) {
        return false;
      }
    }
    pobj = pobj->staticPrototype();
  }

  return true;
}

static bool WatchProtoChangeImpl(JSContext* cx, HandleObject obj) {
  if (!obj->isUsedAsPrototype()) {
    return true;
  }
  if (!ReshapeForProtoMutation(cx, obj)) {
    return false;
  }
  if (obj->is<NativeObject>()) {
    InvalidateMegamorphicCache(cx, obj.as<NativeObject>());

    NativeObject* nobj = &obj->as<NativeObject>();
    if (nobj == nobj->global().maybeGetArrayIteratorPrototype()) {
      nobj->realm()->realmFuses.arrayIteratorPrototypeHasIteratorProto.popFuse(
          cx, nobj->realm()->realmFuses);
    }

    if (nobj == nobj->global().maybeGetIteratorPrototype()) {
      nobj->realm()->realmFuses.iteratorPrototypeHasObjectProto.popFuse(
          cx, nobj->realm()->realmFuses);
    }
  }

  return true;
}

// static
bool Watchtower::watchProtoChangeSlow(JSContext* cx, HandleObject obj) {
  MOZ_ASSERT(watchesProtoChange(obj));

  if (!WatchProtoChangeImpl(cx, obj)) {
    return false;
  }

  if (MOZ_UNLIKELY(obj->useWatchtowerTestingLog())) {
    if (!AddToWatchtowerLog(cx, "proto-change", obj,
                            JS::UndefinedHandleValue)) {
      return false;
    }
  }

  return true;
}

static void MaybePopArrayIteratorFuse(JSContext* cx, NativeObject* obj,
                                      jsid id) {
  if (!id.isWellKnownSymbol(JS::SymbolCode::iterator)) {
    return;
  }

  JSObject* originalArrayPrototype = obj->global().maybeGetArrayPrototype();
  if (!originalArrayPrototype) {
    return;
  }

  if (obj != originalArrayPrototype) {
    return;
  }

  obj->realm()->realmFuses.arrayPrototypeIteratorFuse.popFuse(
      cx, obj->realm()->realmFuses);
}

static void MaybePopArrayIteratorPrototypeNextFuse(JSContext* cx,
                                                   NativeObject* obj, jsid id) {
  JSObject* originalArrayIteratorPrototoype =
      obj->global().maybeGetArrayIteratorPrototype();
  if (!originalArrayIteratorPrototoype) {
    return;
  }

  if (obj != originalArrayIteratorPrototoype) {
    return;
  }

  PropertyKey nextId = NameToId(cx->names().next);
  if (id != nextId) {
    return;
  }

  obj->realm()->realmFuses.arrayPrototypeIteratorNextFuse.popFuse(
      cx, obj->realm()->realmFuses);
}

static void MaybePopFuses(JSContext* cx, NativeObject* obj, jsid id) {
  // Handle a write to Array.prototype[@@iterator]
  MaybePopArrayIteratorFuse(cx, obj, id);
  // Handle a write to Array.prototype[@@iterator].next
  MaybePopArrayIteratorPrototypeNextFuse(cx, obj, id);
}

// static
bool Watchtower::watchPropertyRemoveSlow(JSContext* cx,
                                         Handle<NativeObject*> obj,
                                         HandleId id) {
  MOZ_ASSERT(watchesPropertyRemove(obj));

  if (obj->isUsedAsPrototype() && !id.isInt()) {
    InvalidateMegamorphicCache(cx, obj);
  }

  if (obj->isGenerationCountedGlobal()) {
    obj->as<GlobalObject>().bumpGenerationCount();
  }

  if (MOZ_UNLIKELY(obj->hasFuseProperty())) {
    MaybePopFuses(cx, obj, id);
  }

  if (MOZ_UNLIKELY(obj->useWatchtowerTestingLog())) {
    RootedValue val(cx, IdToValue(id));
    if (!AddToWatchtowerLog(cx, "remove-prop", obj, val)) {
      return false;
    }
  }

  return true;
}

// static
bool Watchtower::watchPropertyChangeSlow(JSContext* cx,
                                         Handle<NativeObject*> obj, HandleId id,
                                         PropertyFlags flags) {
  MOZ_ASSERT(watchesPropertyChange(obj));

  if (obj->isUsedAsPrototype() && !id.isInt()) {
    InvalidateMegamorphicCache(cx, obj);
  }

  if (obj->isGenerationCountedGlobal()) {
    // The global generation counter only cares whether a property
    // changes from data property to accessor or vice-versa. Changing
    // the flags on a property doesn't matter.
    uint32_t propIndex;
    Rooted<PropMap*> map(cx, obj->shape()->lookup(cx, id, &propIndex));
    MOZ_ASSERT(map);
    PropertyInfo prop = map->getPropertyInfo(propIndex);
    bool wasAccessor = prop.isAccessorProperty();
    bool isAccessor = flags.isAccessorProperty();
    if (wasAccessor != isAccessor) {
      obj->as<GlobalObject>().bumpGenerationCount();
    }
  }

  // Property fuses should also be popped on property changes, as value can
  // change via this path.
  if (MOZ_UNLIKELY(obj->hasFuseProperty())) {
    MaybePopFuses(cx, obj, id);
  }

  if (MOZ_UNLIKELY(obj->useWatchtowerTestingLog())) {
    RootedValue val(cx, IdToValue(id));
    if (!AddToWatchtowerLog(cx, "change-prop", obj, val)) {
      return false;
    }
  }

  return true;
}

// static
template <AllowGC allowGC>
bool Watchtower::watchPropertyModificationSlow(
    JSContext* cx, typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    typename MaybeRooted<PropertyKey, allowGC>::HandleType id) {
  MOZ_ASSERT(watchesPropertyModification(obj));

  if (MOZ_UNLIKELY(obj->hasFuseProperty())) {
    MaybePopFuses(cx, obj, id);
  }

  // If we cannot GC, we can't manipulate the log, but we need to be able to
  // call this in places we cannot GC.
  if constexpr (allowGC == AllowGC::CanGC) {
    if (MOZ_UNLIKELY(obj->useWatchtowerTestingLog())) {
      RootedValue val(cx, IdToValue(id));
      if (!AddToWatchtowerLog(cx, "modify-prop", obj, val)) {
        return false;
      }
    }
  }

  return true;
}

template bool Watchtower::watchPropertyModificationSlow<AllowGC::CanGC>(
    JSContext* cx,
    typename MaybeRooted<NativeObject*, AllowGC::CanGC>::HandleType obj,
    typename MaybeRooted<PropertyKey, AllowGC::CanGC>::HandleType id);
template bool Watchtower::watchPropertyModificationSlow<AllowGC::NoGC>(
    JSContext* cx,
    typename MaybeRooted<NativeObject*, AllowGC::NoGC>::HandleType obj,
    typename MaybeRooted<PropertyKey, AllowGC::NoGC>::HandleType id);

// static
bool Watchtower::watchFreezeOrSealSlow(JSContext* cx, Handle<NativeObject*> obj,
                                       IntegrityLevel level) {
  MOZ_ASSERT(watchesFreezeOrSeal(obj));

  // Invalidate the megamorphic set-property cache when freezing a prototype
  // object. Non-writable prototype properties can't be shadowed (through
  // SetProp) so this affects the behavior of add-property cache entries.
  if (level == IntegrityLevel::Frozen && obj->isUsedAsPrototype()) {
    InvalidateMegamorphicCache(cx, obj, /* invalidateGetPropCache = */ false);
  }

  if (MOZ_UNLIKELY(obj->useWatchtowerTestingLog())) {
    if (!AddToWatchtowerLog(cx, "freeze-or-seal", obj,
                            JS::UndefinedHandleValue)) {
      return false;
    }
  }

  return true;
}

// static
bool Watchtower::watchObjectSwapSlow(JSContext* cx, HandleObject a,
                                     HandleObject b) {
  MOZ_ASSERT(watchesObjectSwap(a, b));

  // If we're swapping an object that's used as prototype, we're mutating the
  // proto chains of other objects. Treat this as a proto change to ensure we
  // invalidate shape teleporting and megamorphic caches.
  if (!WatchProtoChangeImpl(cx, a)) {
    return false;
  }
  if (!WatchProtoChangeImpl(cx, b)) {
    return false;
  }

  // Note: we don't invoke the testing callback for swap because the objects may
  // not be safe to expose to JS at this point. See bug 1754699.

  return true;
}

Messung V0.5
C=94 H=99 G=96

¤ Dauer der Verarbeitung: 0.10 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 und die Messung sind noch experimentell.