DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/* -*- 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/. */

#ifndef vm_TaggedProto_h
#define vm_TaggedProto_h

#include "mozilla/Maybe.h"

#include "gc/Tracer.h"

namespace js {

// Information about an object prototype, which can be either a particular
// object, null, or a lazily generated object. The latter is only used by
// certain kinds of proxies.
class TaggedProto {
 public:
  static JSObject* const LazyProto;

  TaggedProto() : proto(nullptr) {}
  TaggedProto(const TaggedProto& other) : proto(other.proto) {}
  explicit TaggedProto(JSObject* proto) : proto(proto) {}

  uintptr_t toWord() const { return uintptr_t(proto); }

  bool isDynamic() const { return proto == LazyProto; }
  bool isObject() const {
    /* Skip nullptr and LazyProto. */
    return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto);
  }
  JSObject* toObject() const {
    MOZ_ASSERT(isObject());
    return proto;
  }
  JSObject* toObjectOrNull() const {
    MOZ_ASSERT(!proto || isObject());
    return proto;
  }
  JSObject* raw() const { return proto; }

  bool operator==(const TaggedProto& other) const {
    return proto == other.proto;
  }
  bool operator!=(const TaggedProto& other) const {
    return proto != other.proto;
  }

  HashNumber hashCode() const;

  void trace(JSTracer* trc) {
    if (isObject()) {
      TraceManuallyBarrieredEdge(trc, &proto, "TaggedProto");
    }
  }

 private:
  JSObject* proto;
};

template <>
struct MovableCellHasher<TaggedProto> {
  using Key = TaggedProto;
  using Lookup = TaggedProto;

  static bool hasHash(const Lookup& l) {
    return !l.isObject() || MovableCellHasher<JSObject*>::hasHash(l.toObject());
  }
  static bool ensureHash(const Lookup& l) {
    return !l.isObject() ||
           MovableCellHasher<JSObject*>::ensureHash(l.toObject());
  }
  static HashNumber hash(const Lookup& l) {
    if (l.isDynamic()) {
      return uint64_t(1);
    }
    if (!l.isObject()) {
      return uint64_t(0);
    }
    return MovableCellHasher<JSObject*>::hash(l.toObject());
  }
  static bool match(const Key& k, const Lookup& l) {
    return k.isDynamic() == l.isDynamic() && k.isObject() == l.isObject() &&
           (!k.isObject() ||
            MovableCellHasher<JSObject*>::match(k.toObject(), l.toObject()));
  }
};

#ifdef DEBUG
MOZ_ALWAYS_INLINE void AssertTaggedProtoIsNotGray(const TaggedProto& proto) {
  if (proto.isObject()) {
    JS::AssertObjectIsNotGray(proto.toObject());
  }
}
#endif

template <>
struct InternalBarrierMethods<TaggedProto> {
  static void preBarrier(TaggedProto& proto);

  static void postBarrier(TaggedProto* vp, TaggedProto prev, TaggedProto next);

  static void readBarrier(const TaggedProto& proto);

  static bool isMarkable(const TaggedProto& proto) { return proto.isObject(); }

#ifdef DEBUG
  static void assertThingIsNotGray(const TaggedProto& proto) {
    AssertTaggedProtoIsNotGray(proto);
  }
#endif
};

template <class Wrapper>
class WrappedPtrOperations<TaggedProto, Wrapper> {
  const TaggedProto& value() const {
    return static_cast<const Wrapper*>(this)->get();
  }

 public:
  uintptr_t toWord() const { return value().toWord(); }
  inline bool isDynamic() const { return value().isDynamic(); }
  inline bool isObject() const { return value().isObject(); }
  inline JSObject* toObject() const { return value().toObject(); }
  inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
  JSObject* raw() const { return value().raw(); }
  HashNumber hashCode() const { return value().hashCode(); }
  uint64_t uniqueId() const { return value().uniqueId(); }
};

// If the TaggedProto is a JSObject pointer, convert to that type and call |f|
// with the pointer. If the TaggedProto is lazy, returns None().
template <typename F>
auto MapGCThingTyped(const TaggedProto& proto, F&& f) {
  if (proto.isObject()) {
    return mozilla::Some(f(proto.toObject()));
  }
  using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
  return mozilla::Maybe<ReturnType>();
}

template <typename F>
bool ApplyGCThingTyped(const TaggedProto& proto, F&& f) {
  return MapGCThingTyped(proto,
                         [&f](auto t) {
                           f(t);
                           return true;
                         })
      .isSome();
}

// Since JSObject pointers are either nullptr or a valid object and since the
// object layout of TaggedProto is identical to a bare object pointer, we can
// safely treat a pointer to an already-rooted object (e.g. HandleObject) as a
// pointer to a TaggedProto.
inline Handle<TaggedProto> AsTaggedProto(HandleObject obj) {
  static_assert(sizeof(JSObject*) == sizeof(TaggedProto),
                "TaggedProto must be binary compatible with JSObject");
  return Handle<TaggedProto>::fromMarkedLocation(
      reinterpret_cast<TaggedProto const*>(obj.address()));
}

}  // namespace js

#endif  // vm_TaggedProto_h