PageRenderTime 50ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/base/empty-array.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 362 lines | 239 code | 55 blank | 68 comment | 34 complexity | 38d99345814343d1a65e0bb90e481b7e MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 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. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/base/empty-array.h"
  17. #include <utility>
  18. #include <type_traits>
  19. #include "hphp/util/assertions.h"
  20. #include "hphp/runtime/base/array-init.h"
  21. #include "hphp/runtime/base/tv-helpers.h"
  22. #include "hphp/runtime/base/array-data.h"
  23. #include "hphp/runtime/base/type-variant.h"
  24. #include "hphp/runtime/base/mixed-array.h"
  25. #include "hphp/runtime/base/mixed-array-defs.h"
  26. #include "hphp/runtime/base/packed-array-defs.h"
  27. #include "hphp/runtime/base/shape.h"
  28. #include "hphp/runtime/base/struct-array.h"
  29. namespace HPHP {
  30. //////////////////////////////////////////////////////////////////////
  31. std::aligned_storage<
  32. sizeof(ArrayData),
  33. alignof(ArrayData)
  34. >::type s_theEmptyArray;
  35. struct EmptyArray::Initializer {
  36. Initializer() {
  37. auto const ad = reinterpret_cast<ArrayData*>(&s_theEmptyArray);
  38. ad->m_sizeAndPos = 0;
  39. ad->m_hdr.init(HeaderKind::Empty, StaticValue);
  40. }
  41. };
  42. EmptyArray::Initializer EmptyArray::s_initializer;
  43. //////////////////////////////////////////////////////////////////////
  44. void EmptyArray::Release(ArrayData*) {
  45. always_assert(!"never try to free the empty array");
  46. }
  47. void EmptyArray::NvGetKey(const ArrayData*, TypedValue* out, ssize_t pos) {
  48. // We have no valid positions---no one should call this function.
  49. not_reached();
  50. }
  51. size_t EmptyArray::Vsize(const ArrayData*) { not_reached(); }
  52. const Variant& EmptyArray::GetValueRef(const ArrayData* ad, ssize_t pos) {
  53. // We have no valid positions---no one should call this function.
  54. not_reached();
  55. }
  56. // EmptyArray::IterAdvance() is reachable; see ArrayData::next() for details
  57. ssize_t EmptyArray::IterAdvance(const ArrayData*, ssize_t prev) {
  58. return 0;
  59. }
  60. // EmptyArray::IterRewind() is NOT reachable; see ArrayData::prev() for details
  61. ssize_t EmptyArray::IterRewind(const ArrayData*, ssize_t prev) {
  62. not_reached();
  63. }
  64. // Even though we always return false in ValidMArrayIter, this function may
  65. // still be called because MArrayIters are constructed in an invalid position,
  66. // and then advanced to the first element.
  67. bool EmptyArray::AdvanceMArrayIter(ArrayData*, MArrayIter& fp) {
  68. return false;
  69. }
  70. // We're always already a static array.
  71. void EmptyArray::OnSetEvalScalar(ArrayData*) { not_reached(); }
  72. ArrayData* EmptyArray::NonSmartCopy(const ArrayData* ad) { not_reached(); }
  73. //////////////////////////////////////////////////////////////////////
  74. NEVER_INLINE
  75. ArrayData* EmptyArray::Copy(const ArrayData*) { return staticEmptyArray(); }
  76. ArrayData* EmptyArray::CopyWithStrongIterators(const ArrayData* ad) {
  77. // We can never have associated strong iterators, so we don't need
  78. // to do anything extra.
  79. return Copy(ad);
  80. }
  81. //////////////////////////////////////////////////////////////////////
  82. /*
  83. * Note: if you try to tail-call these helper routines, gcc will
  84. * unfortunately still generate functions with frames and and makes a
  85. * call instead of a jump. It's because of std::pair (and is still
  86. * the case if you return a custom struct).
  87. *
  88. * For now we're leaving this, because it's essentially free for these
  89. * routines to leave the lval pointer in the second return register,
  90. * and it seems questionable to clone the whole function just to avoid
  91. * the frame creation in these callers. (It works to reinterpret_cast
  92. * these functions to one that returns ArrayData* instead of a pair in
  93. * the cases we don't need the second value, but this seems a tad too
  94. * sketchy for probably-unmeasurable benefits. I'll admit I didn't
  95. * try to measure it though... ;)
  96. */
  97. /*
  98. * Helper for empty array -> packed transitions. Creates an array
  99. * with one element. The element is transferred into the array (should
  100. * already be incref'd).
  101. */
  102. ALWAYS_INLINE
  103. std::pair<ArrayData*,TypedValue*> EmptyArray::MakePackedInl(TypedValue tv) {
  104. auto const cap = kPackedSmallSize;
  105. auto const ad = static_cast<ArrayData*>(
  106. MM().objMalloc(sizeof(ArrayData) + cap * sizeof(TypedValue))
  107. );
  108. assert(cap == CapCode::ceil(cap).code);
  109. ad->m_sizeAndPos = 1; // size=1, pos=0
  110. ad->m_hdr.init(CapCode::exact(cap), HeaderKind::Packed, 0);
  111. auto& lval = *reinterpret_cast<TypedValue*>(ad + 1);
  112. lval.m_data = tv.m_data;
  113. lval.m_type = tv.m_type;
  114. assert(ad->kind() == ArrayData::kPackedKind);
  115. assert(ad->m_size == 1);
  116. assert(ad->m_pos == 0);
  117. assert(ad->getCount() == 0);
  118. assert(PackedArray::checkInvariants(ad));
  119. return { ad, &lval };
  120. }
  121. NEVER_INLINE
  122. std::pair<ArrayData*,TypedValue*> EmptyArray::MakePacked(TypedValue tv) {
  123. return MakePackedInl(tv);
  124. }
  125. /*
  126. * Helper for creating a single-element mixed array with a string key.
  127. *
  128. * Note: the key is not already incref'd, but the value must be.
  129. */
  130. NEVER_INLINE
  131. std::pair<ArrayData*,TypedValue*>
  132. EmptyArray::MakeMixed(StringData* key, TypedValue val) {
  133. auto const ad = smartAllocArray(MixedArray::SmallScale);
  134. MixedArray::InitSmall(ad, 0/*count*/, 1/*size*/, 0/*nextIntKey*/);
  135. auto const data = ad->data();
  136. auto const hash = reinterpret_cast<int32_t*>(data + MixedArray::SmallSize);
  137. auto const khash = key->hash();
  138. auto const mask = MixedArray::SmallMask;
  139. hash[khash & mask] = 0;
  140. data[0].setStrKey(key, khash);
  141. auto& lval = data[0].data;
  142. lval.m_data = val.m_data;
  143. lval.m_type = val.m_type;
  144. assert(ad->m_size == 1);
  145. assert(ad->m_pos == 0);
  146. assert(ad->m_scale == MixedArray::SmallScale);
  147. assert(ad->kind() == ArrayData::kMixedKind);
  148. assert(ad->getCount() == 0);
  149. assert(ad->m_used == 1);
  150. assert(ad->checkInvariants());
  151. return { ad, &lval };
  152. }
  153. /*
  154. * Creating a single-element mixed array with a integer key. The
  155. * value is already incref'd.
  156. */
  157. std::pair<ArrayData*,TypedValue*>
  158. EmptyArray::MakeMixed(int64_t key, TypedValue val) {
  159. auto const ad = smartAllocArray(MixedArray::SmallScale);
  160. MixedArray::InitSmall(ad, 0/*count*/, 1/*size*/, (key >= 0) ? key + 1 : 0);
  161. auto const data = ad->data();
  162. auto const hash = reinterpret_cast<int32_t*>(data + MixedArray::SmallSize);
  163. auto const mask = MixedArray::SmallMask;
  164. hash[key & mask] = 0;
  165. data[0].setIntKey(key);
  166. auto& lval = data[0].data;
  167. lval.m_data = val.m_data;
  168. lval.m_type = val.m_type;
  169. assert(ad->kind() == ArrayData::kMixedKind);
  170. assert(ad->m_size == 1);
  171. assert(ad->m_pos == 0);
  172. assert(ad->getCount() == 0);
  173. assert(ad->m_scale == MixedArray::SmallScale);
  174. assert(ad->m_used == 1);
  175. assert(ad->checkInvariants());
  176. return { ad, &lval };
  177. }
  178. //////////////////////////////////////////////////////////////////////
  179. ArrayData* EmptyArray::SetInt(ArrayData*, int64_t k, Cell c, bool) {
  180. // TODO(#3888164): we should make it so we don't need KindOfUninit checks
  181. if (c.m_type == KindOfUninit) c.m_type = KindOfNull;
  182. tvRefcountedIncRef(&c);
  183. auto const ret = k == 0 ? EmptyArray::MakePacked(c)
  184. : EmptyArray::MakeMixed(k, c);
  185. return ret.first;
  186. }
  187. ArrayData* EmptyArray::SetStr(ArrayData*,
  188. StringData* k,
  189. Cell val,
  190. bool copy) {
  191. tvRefcountedIncRef(&val);
  192. // TODO(#3888164): we should make it so we don't need KindOfUninit checks
  193. if (val.m_type == KindOfUninit) val.m_type = KindOfNull;
  194. return EmptyArray::MakeMixed(k, val).first;
  195. }
  196. ArrayData* EmptyArray::LvalInt(ArrayData*, int64_t k, Variant*& retVar, bool) {
  197. auto const ret = k == 0 ? EmptyArray::MakePacked(make_tv<KindOfNull>())
  198. : EmptyArray::MakeMixed(k, make_tv<KindOfNull>());
  199. retVar = &tvAsVariant(ret.second);
  200. return ret.first;
  201. }
  202. ArrayData* EmptyArray::LvalStr(ArrayData*,
  203. StringData* k,
  204. Variant*& retVar,
  205. bool) {
  206. auto const ret = EmptyArray::MakeMixed(k, make_tv<KindOfNull>());
  207. retVar = &tvAsVariant(ret.second);
  208. return ret.first;
  209. }
  210. ArrayData* EmptyArray::LvalNew(ArrayData*, Variant*& retVar, bool) {
  211. auto const ret = EmptyArray::MakePacked(make_tv<KindOfNull>());
  212. retVar = &tvAsVariant(ret.second);
  213. return ret.first;
  214. }
  215. ArrayData* EmptyArray::SetRefInt(ArrayData*,
  216. int64_t k,
  217. Variant& var,
  218. bool) {
  219. auto ref = *var.asRef();
  220. tvIncRef(&ref);
  221. auto const ret = k == 0 ? EmptyArray::MakePacked(ref)
  222. : EmptyArray::MakeMixed(k, ref);
  223. return ret.first;
  224. }
  225. ArrayData* EmptyArray::SetRefStr(ArrayData*,
  226. StringData* k,
  227. Variant& var,
  228. bool) {
  229. auto ref = *var.asRef();
  230. tvIncRef(&ref);
  231. return EmptyArray::MakeMixed(k, ref).first;
  232. }
  233. ArrayData* EmptyArray::Append(ArrayData*, const Variant& vin, bool copy) {
  234. auto cell = *vin.asCell();
  235. tvRefcountedIncRef(&cell);
  236. // TODO(#3888164): we should make it so we don't need KindOfUninit checks
  237. if (cell.m_type == KindOfUninit) cell.m_type = KindOfNull;
  238. return EmptyArray::MakePackedInl(cell).first;
  239. }
  240. ArrayData* EmptyArray::AppendRef(ArrayData*, Variant& v, bool copy) {
  241. auto ref = *v.asRef();
  242. tvIncRef(&ref);
  243. return EmptyArray::MakePacked(ref).first;
  244. }
  245. ArrayData* EmptyArray::AppendWithRef(ArrayData*, const Variant& v, bool copy) {
  246. auto tv = make_tv<KindOfNull>();
  247. tvAsVariant(&tv).setWithRef(v);
  248. return EmptyArray::MakePacked(tv).first;
  249. }
  250. //////////////////////////////////////////////////////////////////////
  251. ArrayData* EmptyArray::PlusEq(ArrayData*, const ArrayData* elems) {
  252. elems->incRefCount();
  253. return const_cast<ArrayData*>(elems);
  254. }
  255. ArrayData* EmptyArray::Merge(ArrayData*, const ArrayData* elems) {
  256. // Packed arrays don't need renumbering, so don't make a copy.
  257. if (elems->isPacked() || elems->isStruct()) {
  258. elems->incRefCount();
  259. return const_cast<ArrayData*>(elems);
  260. }
  261. // Fast path the common case that elems is mixed.
  262. if (elems->isMixed()) {
  263. auto const copy = MixedArray::Copy(elems);
  264. copy->incRefCount();
  265. MixedArray::Renumber(copy);
  266. return copy;
  267. }
  268. auto copy = elems->copy();
  269. copy->incRefCount();
  270. copy->renumber();
  271. return copy;
  272. }
  273. ArrayData* EmptyArray::PopOrDequeue(ArrayData* ad, Variant& value) {
  274. value = uninit_null();
  275. return ad;
  276. }
  277. ArrayData* EmptyArray::Prepend(ArrayData*, const Variant& vin, bool) {
  278. auto cell = *vin.asCell();
  279. tvRefcountedIncRef(&cell);
  280. // TODO(#3888164): we should make it so we don't need KindOfUninit checks
  281. if (cell.m_type == KindOfUninit) cell.m_type = KindOfNull;
  282. return EmptyArray::MakePacked(cell).first;
  283. }
  284. //////////////////////////////////////////////////////////////////////
  285. ArrayData* EmptyArray::ZSetInt(ArrayData* ad, int64_t k, RefData* v) {
  286. auto const arr = MixedArray::MakeReserveMixed(MixedArray::SmallSize);
  287. arr->setRefCount(0);
  288. DEBUG_ONLY auto const tmp = arr->zSet(k, v);
  289. assert(tmp == arr);
  290. return arr;
  291. }
  292. ArrayData* EmptyArray::ZSetStr(ArrayData* ad, StringData* k, RefData* v) {
  293. auto const arr = MixedArray::MakeReserveMixed(MixedArray::SmallSize);
  294. arr->setRefCount(0);
  295. DEBUG_ONLY auto const tmp = arr->zSet(k, v);
  296. assert(tmp == arr);
  297. return arr;
  298. }
  299. ArrayData* EmptyArray::ZAppend(ArrayData* ad, RefData* v, int64_t* key_ptr) {
  300. auto const arr = MixedArray::MakeReserveMixed(MixedArray::SmallSize);
  301. arr->setRefCount(0);
  302. DEBUG_ONLY auto const tmp = arr->zAppend(v, key_ptr);
  303. assert(tmp == arr);
  304. return arr;
  305. }
  306. //////////////////////////////////////////////////////////////////////
  307. }