summaryrefslogtreecommitdiffstats
path: root/js/src/vm/TaggedProto.h
blob: 42aecf998aa5f560881a1bd62fe01022b6728f26 (plain)
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
168
169
170
171
172
173
/* -*- 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/Barrier.h"
#include "js/HashTable.h"
#include "js/RootingAPI.h"

class JSObject;

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) = default;
  explicit TaggedProto(JSObject* proto) : proto(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);

 private:
  JSObject* proto;
};

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

  static bool maybeGetHash(const Lookup& l, HashNumber* hashOut) {
    if (!l.isObject()) {
      *hashOut = hash(l);
      return true;
    }

    return StableCellHasher<JSObject*>::maybeGetHash(l.toObject(), hashOut);
  }
  static bool ensureHash(const Lookup& l, HashNumber* hashOut) {
    if (!l.isObject()) {
      *hashOut = hash(l);
      return true;
    }
    return StableCellHasher<JSObject*>::ensureHash(l.toObject(), hashOut);
  }
  static HashNumber hash(const Lookup& l) {
    if (l.isDynamic()) {
      return uint64_t(1);
    }
    if (!l.isObject()) {
      return uint64_t(0);
    }
    return StableCellHasher<JSObject*>::hash(l.toObject());
  }
  static bool match(const Key& k, const Lookup& l) {
    return k.isDynamic() == l.isDynamic() && k.isObject() == l.isObject() &&
           (!k.isObject() ||
            StableCellHasher<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