Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / static-string-table.cpp
blobe6950e1fac4cd2a7eabc4aa35b0e47ed5e992fa8
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 #include "hphp/runtime/base/static-string-table.h"
18 #include "hphp/runtime/base/perf-warning.h"
19 #include "hphp/runtime/base/rds.h"
20 #include "hphp/runtime/base/runtime-option.h"
21 #include "hphp/runtime/vm/debug/debug.h"
22 #include "hphp/runtime/vm/reverse-data-map.h"
24 #include "hphp/runtime/server/memory-stats.h"
26 #include "hphp/util/low-ptr.h"
28 #include <folly/AtomicHashMap.h>
30 #include <type_traits>
32 namespace HPHP {
34 //////////////////////////////////////////////////////////////////////
36 namespace {
38 // the string key will one of these values:
39 // * a valid LowPtr<StringData>, or
40 // * -1, -2, or -3 AHM magic values.
41 // Note that only the magic values have 1s in the low 3 bits
42 // since StringData's are at least 8-aligned.
44 using StrInternKey = LowStringPtr::storage_type;
46 // Return true if k is one of AHM's magic values. Valid pointers are
47 // 8-aligned, so test the low 3 bits.
48 bool isMagicKey(StrInternKey k) {
49 return (k & 7) != 0;
52 const StringData* to_sdata(StrInternKey key) {
53 assert(!isMagicKey(key));
54 static_assert(std::is_unsigned<StrInternKey>(), "cast must zero-extend");
55 return reinterpret_cast<const StringData*>(key);
58 struct strintern_eq {
59 bool operator()(StrInternKey k1, StrInternKey k2) const {
60 assert(!isMagicKey(k2)); // no magic values on rhs
61 return operator()(k1, to_sdata(k2));
64 bool operator()(StrInternKey k1, const StringData* string2) const {
65 if (isMagicKey(k1)) return false; // magic values
66 auto const sd1 = to_sdata(k1);
67 auto const len1 = sd1->size();
68 auto const data1 = sd1->data();
69 if (len1 != string2->size()) return false;
70 // only use wordsame on 8-byte aligned addresses
71 return wordsame(data1, string2->data(), len1);
74 bool operator()(StrInternKey k1, folly::StringPiece slice2) const {
75 if (isMagicKey(k1)) return false; // magic values
76 auto const sd1 = to_sdata(k1);
77 auto const len1 = sd1->size();
78 auto const data1 = sd1->data();
79 if (len1 != slice2.size()) return false;
80 return !memcmp(data1, slice2.begin(), len1);
84 struct strintern_hash {
85 size_t operator()(StrInternKey k) const {
86 assert(!isMagicKey(k)); // no magic values get here
87 return operator()(to_sdata(k));
90 size_t operator()(const StringData* sd) const {
91 return sd->hash();
94 size_t operator()(folly::StringPiece slice) const {
95 return StringData::hash(slice.data(), slice.size());
99 // The uint32_t is used to hold RDS offsets for constants
100 using StringDataMap = folly::AtomicHashMap<
101 StrInternKey,
102 rds::Link<TypedValue>,
103 strintern_hash,
104 strintern_eq,
105 HugeAllocator<char>
108 struct EmbeddedStringMap {
109 explicit operator bool() const { return inited; }
111 StringDataMap* operator->() {
112 assert(inited);
113 return reinterpret_cast<StringDataMap*>(&data);
115 StringDataMap& operator*() { assert(inited); return *operator->(); }
117 void emplace(uint32_t size, const StringDataMap::Config& config) {
118 assert(!inited);
119 new (&data) StringDataMap(size, config);
120 inited = true;
123 void clear() {
124 if (inited) {
125 operator*().~StringDataMap();
126 inited = false;
130 private:
131 typename std::aligned_storage<
132 sizeof(StringDataMap),
133 alignof(StringDataMap)
134 >::type data;
135 bool inited;
138 EmbeddedStringMap s_stringDataMap;
140 // If a string is static it better be the one in the table.
141 DEBUG_ONLY bool checkStaticStr(const StringData* s) {
142 assert(s->isStatic());
143 assert(s_stringDataMap);
144 auto DEBUG_ONLY const it = s_stringDataMap->find(s);
145 assert(it != s_stringDataMap->end());
146 assert(to_sdata(it->first) == s);
147 return true;
150 StringData** precompute_chars();
151 StringData** precompute_chars() {
152 StringData** raw = new StringData*[256];
153 for (int i = 0; i < 256; i++) {
154 char s[2] = { (char)i, 0 };
155 raw[i] = makeStaticStringSafe(&s[0], 1);
157 return raw;
160 StringData* insertStaticString(StringData* sd) {
161 assert(sd->isStatic());
162 auto pair = s_stringDataMap->insert(
163 safe_cast<StrInternKey>(reinterpret_cast<uintptr_t>(sd)),
164 rds::Link<TypedValue>(rds::kUninitHandle)
167 if (!pair.second) {
168 sd->destructStatic();
169 } else {
170 MemoryStats::GetInstance()->LogStaticStringAlloc(
171 sd->size() + sizeof(StringData)
173 if (RuntimeOption::EvalEnableReverseDataMap) {
174 data_map::register_start(sd);
176 static std::atomic<bool> signaled{false};
177 checkAHMSubMaps(*s_stringDataMap, "static string table", signaled);
179 assert(to_sdata(pair.first->first) != nullptr);
181 return const_cast<StringData*>(to_sdata(pair.first->first));
184 inline StringData* insertStaticStringSlice(folly::StringPiece slice) {
185 return insertStaticString(StringData::MakeStatic(slice));
188 void create_string_data_map() {
189 always_assert(!s_stringDataMap);
190 StringDataMap::Config config;
191 config.growthFactor = 1;
192 config.entryCountThreadCacheSize = 10;
193 MemoryStats::GetInstance()->ResetStaticStringSize();
195 s_stringDataMap.emplace(RuntimeOption::EvalInitialStaticStringTableSize,
196 config);
197 insertStaticString(StringData::MakeEmpty());
202 //////////////////////////////////////////////////////////////////////
204 StringData** precomputed_chars = precompute_chars();
206 size_t makeStaticStringCount() {
207 if (!s_stringDataMap) return 0;
208 return s_stringDataMap->size();
211 StringData* makeStaticString(const StringData* str) {
212 if (str->isStatic()) {
213 assert(checkStaticStr(str));
214 return const_cast<StringData*>(str);
216 auto const it = s_stringDataMap->find(str);
217 if (it != s_stringDataMap->end()) {
218 return const_cast<StringData*>(to_sdata(it->first));
220 return insertStaticStringSlice(str->slice());
223 StringData* makeStaticString(folly::StringPiece slice) {
224 auto const it = s_stringDataMap->find(slice);
225 if (it != s_stringDataMap->end()) {
226 return const_cast<StringData*>(to_sdata(it->first));
228 return insertStaticStringSlice(slice);
231 StringData* lookupStaticString(const StringData *str) {
232 assert(s_stringDataMap && !str->isStatic());
233 auto const it = s_stringDataMap->find(str);
234 if (it != s_stringDataMap->end()) {
235 return const_cast<StringData*>(to_sdata(it->first));
237 return nullptr;
240 StringData* makeStaticString(const String& str) {
241 assert(!str.isNull());
242 return makeStaticString(str.get());
245 StringData* makeStaticString(const char* str, size_t len) {
246 assert(len <= StringData::MaxSize);
247 return makeStaticString(folly::StringPiece{str, len});
250 StringData* makeStaticString(const std::string& str) {
251 assert(str.size() <= StringData::MaxSize);
252 return makeStaticString(folly::StringPiece{str.c_str(), str.size()});
255 StringData* makeStaticString(const char* str) {
256 return makeStaticString(str, strlen(str));
259 StringData* makeStaticString(char c) {
260 // TODO(#2880477): should this be inlined?
261 return precomputed_chars[(uint8_t)c];
264 StringData* makeStaticStringSafe(const char* str, size_t len) {
265 assert(len <= StringData::MaxSize);
266 if (UNLIKELY(!s_stringDataMap)) {
267 create_string_data_map();
269 return makeStaticString(str, len);
272 StringData* makeStaticStringSafe(const char* str) {
273 if (UNLIKELY(!s_stringDataMap)) {
274 create_string_data_map();
276 return makeStaticString(str);
279 bool bindPersistentCns(const StringData* cnsName, const Cell& value) {
280 assert(s_stringDataMap);
281 auto const it = s_stringDataMap->find(cnsName);
282 assertx(it != s_stringDataMap->end());
283 it->second.bind(
284 [&] {
285 auto const link =
286 rds::alloc<TypedValue, kTVSimdAlign>(rds::Mode::Persistent);
287 *link = value;
288 rds::recordRds(link.handle(), sizeof(TypedValue),
289 "Cns", cnsName->data());
290 return link.handle();
293 return it->second.isPersistent();
296 rds::Handle lookupCnsHandle(const StringData* cnsName) {
297 assert(s_stringDataMap);
298 auto const it = s_stringDataMap->find(cnsName);
299 if (it != s_stringDataMap->end()) {
300 return it->second.maybeHandle();
302 return rds::kUninitHandle;
305 rds::Handle makeCnsHandle(const StringData* cnsName) {
306 auto const val = lookupCnsHandle(cnsName);
307 if (rds::isHandleBound(val)) return val;
308 if (!cnsName->isStatic()) {
309 // Its a dynamic constant, that doesn't correspond to
310 // an already allocated handle. We'll allocate it in
311 // the request local rds::s_constants instead.
312 return rds::kUninitHandle;
314 auto const it = s_stringDataMap->find(cnsName);
315 assert(it != s_stringDataMap->end());
316 if (!it->second.bound()) {
317 it->second.bind<kTVSimdAlign>(rds::Mode::Normal);
319 rds::recordRds(it->second.handle(), sizeof(TypedValue),
320 "Cns", cnsName->data());
322 return it->second.handle();
325 std::vector<StringData*> lookupDefinedStaticStrings() {
326 assert(s_stringDataMap);
327 std::vector<StringData*> ret;
329 for (auto it = s_stringDataMap->begin();
330 it != s_stringDataMap->end(); ++it) {
331 ret.push_back(const_cast<StringData*>(to_sdata(it->first)));
334 return ret;
338 const StaticString s_user("user");
339 const StaticString s_Core("Core");
341 Array lookupDefinedConstants(bool categorize /*= false */) {
342 assert(s_stringDataMap);
343 Array usr(rds::s_constants());
344 Array sys;
346 for (auto it = s_stringDataMap->begin();
347 it != s_stringDataMap->end(); ++it) {
348 if (it->second.bound()) {
349 if (!it->second.isInit()) continue;
351 Array *tbl = (categorize && it->second.isPersistent()) ? &sys : &usr;
352 auto& tv = *it->second;
354 if (tv.m_type != KindOfUninit) {
355 StrNR key(const_cast<StringData*>(to_sdata(it->first)));
356 tbl->set(key, tvAsVariant(&tv), true);
357 } else {
358 assertx(tv.m_data.pref);
359 StrNR key(const_cast<StringData*>(to_sdata(it->first)));
360 auto callback =
361 reinterpret_cast<Native::ConstantCallback>(tv.m_data.pref);
362 auto cns = callback();
363 if (cns.isInitialized()) {
364 tbl->set(key, cns, true);
370 if (categorize) {
371 Array ret;
372 ret.set(s_user, usr);
373 ret.set(s_Core, sys);
374 return ret;
375 } else {
376 return usr;
380 size_t countStaticStringConstants() {
381 if (!s_stringDataMap) return 0;
382 size_t count = 0;
383 for (auto it = s_stringDataMap->begin();
384 it != s_stringDataMap->end(); ++it) {
385 if (it->second.bound()) {
386 ++count;
389 return count;
392 void refineStaticStringTableSize() {
393 if (RuntimeOption::EvalInitialStaticStringTableSize ==
394 kDefaultInitialStaticStringTableSize ||
395 !s_stringDataMap) {
396 return;
399 std::vector<StringDataMap::value_type>
400 oldStringTable(s_stringDataMap->begin(), s_stringDataMap->end());
402 s_stringDataMap.clear();
403 create_string_data_map();
405 for (auto& kv : oldStringTable) {
406 s_stringDataMap->insert(kv.first, kv.second);
410 //////////////////////////////////////////////////////////////////////