Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / repo-auth-type.h
blob86aad752cd9ae39ba08e0af74866345a6f4280e0
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_REPO_AUTH_TYPE_H_
17 #define incl_HPHP_REPO_AUTH_TYPE_H_
19 #include <limits>
20 #include <string>
22 #include <folly/Optional.h>
24 #include "hphp/util/assertions.h"
25 #include "hphp/util/compact-tagged-ptrs.h"
27 #include "hphp/runtime/base/datatype.h"
28 #include "hphp/runtime/base/runtime-option.h"
30 namespace HPHP {
32 //////////////////////////////////////////////////////////////////////
34 struct StringData;
35 struct TypedValue;
36 struct Unit;
37 struct UnitEmitter;
38 struct RepoAuthType;
40 //////////////////////////////////////////////////////////////////////
43 * Representation of types inferred statically for RepoAuthoritative
44 * mode, for use in runtime data structures, or the bytecode stream
45 * (see the AssertRAT{L,Stk} opcodes).
47 * This is encoded to be space efficient, so there's a small
48 * abstraction layer.
51 //////////////////////////////////////////////////////////////////////
53 struct RepoAuthType {
54 struct Array;
56 #define REPO_AUTH_TYPE_TAGS \
57 TAG(Uninit) \
58 TAG(InitNull) \
59 TAG(Null) \
60 TAG(Int) \
61 TAG(OptInt) \
62 TAG(Dbl) \
63 TAG(OptDbl) \
64 TAG(Res) \
65 TAG(OptRes) \
66 TAG(Bool) \
67 TAG(OptBool) \
68 TAG(SStr) \
69 TAG(OptSStr) \
70 TAG(Str) \
71 TAG(OptStr) \
72 TAG(Obj) \
73 TAG(OptObj) \
74 TAG(InitUnc) \
75 TAG(Unc) \
76 TAG(UncArrKey) \
77 TAG(ArrKey) \
78 TAG(OptUncArrKey) \
79 TAG(OptArrKey) \
80 TAG(InitCell) \
81 TAG(Cell) \
82 TAG(Ref) \
83 TAG(InitGen) \
84 TAG(Gen) \
85 /* Types where array() may be non-null. */ \
86 TAG(SArr) \
87 TAG(OptSArr) \
88 TAG(Arr) \
89 TAG(OptArr) \
90 TAG(SVArr) \
91 TAG(OptSVArr) \
92 TAG(VArr) \
93 TAG(OptVArr) \
94 TAG(SDArr) \
95 TAG(OptSDArr) \
96 TAG(DArr) \
97 TAG(OptDArr) \
98 TAG(SVec) \
99 TAG(OptSVec) \
100 TAG(Vec) \
101 TAG(OptVec) \
102 TAG(SDict) \
103 TAG(OptSDict) \
104 TAG(Dict) \
105 TAG(OptDict) \
106 TAG(SKeyset) \
107 TAG(OptSKeyset) \
108 TAG(Keyset) \
109 TAG(OptKeyset) \
110 /* Types where clsName() will be non-null. */ \
111 TAG(ExactObj) \
112 TAG(SubObj) \
113 TAG(OptExactObj) \
114 TAG(OptSubObj)
116 enum class Tag : uint8_t {
117 #define TAG(x) x,
118 REPO_AUTH_TYPE_TAGS
119 #undef TAG
122 explicit RepoAuthType(Tag tag = Tag::Gen, const StringData* sd = nullptr) {
123 m_data.set(static_cast<uint8_t>(tag), sd);
124 switch (tag) {
125 case Tag::OptSubObj: case Tag::OptExactObj:
126 case Tag::SubObj: case Tag::ExactObj:
127 assert(sd != nullptr);
128 break;
129 default:
130 break;
134 explicit RepoAuthType(Tag tag, const Array* ar) {
135 m_data.set(static_cast<uint8_t>(tag), ar);
136 assert(mayHaveArrData());
139 Tag tag() const { return toResolvedTag(m_data.tag()); }
141 bool operator==(RepoAuthType) const;
142 bool operator!=(RepoAuthType o) const { return !(*this == o); }
143 size_t hash() const;
146 * Class Names.
149 const StringData* clsName() const {
150 assert(hasClassName());
151 return static_cast<const StringData*>(m_data.ptr());
154 bool hasClassName() const {
155 switch (tag()) {
156 case Tag::SubObj: case Tag::ExactObj:
157 case Tag::OptSubObj: case Tag::OptExactObj:
158 return true;
159 default:
160 return false;
162 not_reached();
166 * Arrays.
169 const Array* array() const {
170 assert(resolved());
171 return static_cast<const Array*>(m_data.ptr());
174 // Returns a valid id if there is a corresponding Array* somewhere,
175 // or return kInvalidArrayId if Array* is null or if it is unresolved.
176 const uint32_t arrayId() const;
177 static constexpr auto kInvalidArrayId = std::numeric_limits<uint32_t>::max();
179 // Turn an array RAT represented by ID into equivalent array RAT represented
180 // by its actual Array*. Should only be called when it is indeed not resolved
181 // yet, which should be the place where an RAT is initally loaded from Repo.
182 void resolveArray(const UnitEmitter& ue);
184 bool mayHaveArrData() const {
185 switch (tag()) {
186 case Tag::OptArr: case Tag::OptSArr: case Tag::Arr: case Tag::SArr:
187 case Tag::OptVArr: case Tag::OptSVArr: case Tag::VArr: case Tag::SVArr:
188 case Tag::OptDArr: case Tag::OptSDArr: case Tag::DArr: case Tag::SDArr:
189 case Tag::OptVec: case Tag::OptSVec: case Tag::Vec: case Tag::SVec:
190 case Tag::OptDict: case Tag::OptSDict: case Tag::Dict: case Tag::SDict:
191 case Tag::OptKeyset: case Tag::OptSKeyset:
192 case Tag::Keyset: case Tag::SKeyset:
193 return true;
194 default:
195 return false;
197 not_reached();
200 // Return true if m_data contains non-null Array*.
201 bool hasArrData() const {
202 return mayHaveArrData() && resolved() && m_data.ptr();
206 * Serialization/Deserialization
209 template <class SerDe>
210 void serde(SerDe& sd) {
211 auto t = tag();
212 sd(t);
214 if (SerDe::deserializing) {
215 // mayHaveArrData and hasClassName need to read tag().
216 m_data.set(static_cast<uint8_t>(t), nullptr);
219 // the 0x40 bit for resolved/unresolved Array* should not be visible
220 // to the outside world.
221 assert(resolved());
223 if (mayHaveArrData()) {
224 // serialization
225 if (!SerDe::deserializing) {
226 // either a valid id for non-null array, or a kInvalidArrayId for null
227 uint32_t id = arrayId();
228 sd(id);
229 return;
232 // deserialization
233 uint32_t id;
234 sd(id);
236 // nullptr case, already done
237 if (id == kInvalidArrayId) return;
239 // id case
240 // this is the only case where we set the 0x40 bit
241 auto ptr = reinterpret_cast<const void*>(id);
242 m_data.set(toIdTag(t), ptr);
243 return;
246 if (hasClassName()) {
247 auto c = clsName();
248 sd(c);
249 m_data.set(static_cast<uint8_t>(t), reinterpret_cast<const void*>(c));
253 private:
254 #define TAG(x) static_assert((static_cast<uint8_t>(Tag::x) & 0x40) == 0, "");
255 REPO_AUTH_TYPE_TAGS
256 #undef TAG
258 friend struct ArrayTypeTable;
259 friend struct Array;
261 template <class LookupFn>
262 void doResolve(LookupFn fn) {
263 if (!mayHaveArrData() || resolved()) return;
265 auto const id = arrayId();
266 assert(id != kInvalidArrayId); // this case is handled in deser time.
267 auto const array = fn(id);
268 m_data.set(static_cast<uint8_t>(tag()), array);
271 // false if m_data contains an uint32_t id for array type.
272 // true otherwise (it may not even be an array type).
273 // Note that the 0x80 bit is used by encodeRAT and decodeRAT,
274 // and the 0x20 bit is used in the Tag enum.
275 const bool resolved() const {
276 return (m_data.tag() & 0x40) == 0;
278 static uint8_t toIdTag(Tag tag) {
279 return static_cast<uint8_t>(tag) | 0x40;
281 static Tag toResolvedTag(uint8_t tag) {
282 return static_cast<Tag>(tag & ~0x40);
285 private:
286 // This is the type tag (for the lower 6 bits) plus two flag bits (0x80 used
287 // by encodeRAT/decodeRAT and 0x40 used by ourselves), plus an optional
288 // pointer to a class name (for the obj_* types), or an optional pointer to
289 // array information for array types, or alternatively, an optional id to the
290 // array information with 0x40 flag set to 1 to differentiate from the pointer
291 // case.
292 CompactTaggedPtr<const void,uint8_t> m_data;
295 //////////////////////////////////////////////////////////////////////
298 * Return whether a TypedValue is a legal match for a RepoAuthType.
299 * This can be used for validating that assumptions from static
300 * analysis are not violated (for example, by unserializing objects
301 * with changed private property types).
303 * Note: this function returns true on array types without checking
304 * every element. This is ok for private properties for now because
305 * we don't ever infer inner-array types on properties, but if that
306 * changes new mechanisms may be needed. Relevant to both:
307 * TODO(#3696042,#2516227).
309 bool tvMatchesRepoAuthType(TypedValue, RepoAuthType);
312 * Produce a human-readable string from a RepoAuthType. (Intended for
313 * debugging purposes.)
315 std::string show(RepoAuthType);
317 //////////////////////////////////////////////////////////////////////
321 #endif