PageRenderTime 51ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/runtime/base/type-variant.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 1206 lines | 958 code | 151 blank | 97 comment | 176 complexity | a6b489f75ad37371d7edaa64d10c462c 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/type-variant.h"
  17. #include "hphp/parser/hphp.tab.hpp"
  18. #include "hphp/runtime/base/array-iterator.h"
  19. #include "hphp/runtime/base/collections.h"
  20. #include "hphp/runtime/base/comparisons.h"
  21. #include "hphp/runtime/base/dummy-resource.h"
  22. #include "hphp/runtime/base/externals.h"
  23. #include "hphp/runtime/base/runtime-option.h"
  24. #include "hphp/runtime/base/strings.h"
  25. #include "hphp/runtime/base/thread-info.h"
  26. #include "hphp/runtime/base/tv-arith.h"
  27. #include "hphp/runtime/base/variable-serializer.h"
  28. #include "hphp/runtime/base/variable-unserializer.h"
  29. #include "hphp/runtime/base/zend-functions.h"
  30. #include "hphp/runtime/base/zend-string.h"
  31. #include "hphp/runtime/ext/ext_collections.h"
  32. #include "hphp/runtime/ext/std/ext_std_variable.h"
  33. #include "hphp/runtime/vm/native-data.h"
  34. #include "hphp/runtime/vm/runtime.h"
  35. #include "hphp/runtime/vm/repo.h"
  36. #include "hphp/runtime/vm/repo-global-data.h"
  37. #include "hphp/system/systemlib.h"
  38. #include "hphp/util/abi-cxx.h"
  39. #include "hphp/util/logger.h"
  40. #include <limits>
  41. #include <utility>
  42. #include <vector>
  43. namespace HPHP {
  44. const Variant null_variant; // uninitialized variant
  45. const Variant init_null_variant((Variant::NullInit())); // php null
  46. const VarNR null_varNR;
  47. const VarNR true_varNR(true);
  48. const VarNR false_varNR(false);
  49. const VarNR INF_varNR(std::numeric_limits<double>::infinity());
  50. const VarNR NEGINF_varNR(std::numeric_limits<double>::infinity());
  51. const VarNR NAN_varNR(std::numeric_limits<double>::quiet_NaN());
  52. const Variant empty_string_variant_ref(staticEmptyString(),
  53. Variant::StaticStrInit{});
  54. static void unserializeProp(VariableUnserializer *uns,
  55. ObjectData *obj, const String& key,
  56. Class* ctx, const String& realKey,
  57. int nProp) NEVER_INLINE;
  58. ///////////////////////////////////////////////////////////////////////////////
  59. // static strings
  60. const StaticString
  61. s_offsetGet("offsetGet"),
  62. s_offsetSet("offsetSet"),
  63. s_offsetUnset("offsetUnset"),
  64. s_s("s"),
  65. s_scalar("scalar"),
  66. s_1("1"),
  67. s_unserialize("unserialize"),
  68. s_PHP_Incomplete_Class("__PHP_Incomplete_Class"),
  69. s_PHP_Incomplete_Class_Name("__PHP_Incomplete_Class_Name");
  70. ///////////////////////////////////////////////////////////////////////////////
  71. Variant::Variant(StringData *v) noexcept {
  72. if (v) {
  73. m_data.pstr = v;
  74. if (v->isStatic()) {
  75. m_type = KindOfStaticString;
  76. } else {
  77. m_type = KindOfString;
  78. v->incRefCount();
  79. }
  80. } else {
  81. m_type = KindOfNull;
  82. }
  83. }
  84. // the version of the high frequency function that is not inlined
  85. NEVER_INLINE
  86. Variant::Variant(const Variant& v) noexcept {
  87. constructValHelper(v);
  88. }
  89. /*
  90. * The destruct functions below all arbitrarily take RefData* as an
  91. * example of a refcounted object, then just cast to the proper type.
  92. * This is safe because we have compile time assertions that guarantee that
  93. * the _count field will always be exactly FAST_REFCOUNT_OFFSET bytes from
  94. * the beginning of the object for the StringData, ArrayData, ObjectData,
  95. * ResourceData, and RefData classes.
  96. */
  97. static_assert(TYPE_TO_DESTR_IDX(KindOfString) == 1, "String destruct index");
  98. static_assert(TYPE_TO_DESTR_IDX(KindOfArray) == 2, "Array destruct index");
  99. static_assert(TYPE_TO_DESTR_IDX(KindOfObject) == 3, "Object destruct index");
  100. static_assert(TYPE_TO_DESTR_IDX(KindOfResource) == 4,
  101. "Resource destruct index");
  102. static_assert(TYPE_TO_DESTR_IDX(KindOfRef) == 5, "Ref destruct index");
  103. static_assert(kDestrTableSize == 6,
  104. "size of g_destructors[] must be kDestrTableSize");
  105. RawDestructor g_destructors[] = {
  106. nullptr,
  107. (RawDestructor)getMethodPtr(&StringData::release),
  108. (RawDestructor)getMethodPtr(&ArrayData::release),
  109. (RawDestructor)getMethodPtr(&ObjectData::release), // may replace at runtime
  110. (RawDestructor)getMethodPtr(&ResourceData::release),
  111. (RawDestructor)getMethodPtr(&RefData::release),
  112. };
  113. void tweak_variant_dtors() {
  114. if (RuntimeOption::EnableObjDestructCall) return;
  115. g_destructors[TYPE_TO_DESTR_IDX(KindOfObject)] =
  116. (RawDestructor)getMethodPtr(&ObjectData::releaseNoObjDestructCheck);
  117. }
  118. Variant::~Variant() noexcept {
  119. tvRefcountedDecRef(asTypedValue());
  120. if (debug) {
  121. memset(this, kTVTrashFill2, sizeof(*this));
  122. }
  123. }
  124. void tvDecRefHelper(DataType type, uint64_t datum) noexcept {
  125. assert(type == KindOfString || type == KindOfArray ||
  126. type == KindOfObject || type == KindOfResource ||
  127. type == KindOfRef);
  128. if (((ArrayData*)datum)->decReleaseCheck()) {
  129. g_destructors[typeToDestrIndex(type)]((void*)datum);
  130. }
  131. }
  132. Variant &Variant::assign(const Variant& v) noexcept {
  133. AssignValHelper(this, &v);
  134. return *this;
  135. }
  136. Variant& Variant::assignRef(Variant& v) noexcept {
  137. assignRefHelper(v);
  138. return *this;
  139. }
  140. Variant& Variant::setWithRef(const Variant& v) noexcept {
  141. setWithRefHelper(v, IS_REFCOUNTED_TYPE(m_type));
  142. return *this;
  143. }
  144. #define IMPLEMENT_SET_IMPL(name, argType, argName, setOp) \
  145. void Variant::name(argType argName) noexcept { \
  146. if (isPrimitive()) { \
  147. setOp; \
  148. } else if (m_type == KindOfRef) { \
  149. m_data.pref->var()->name(argName); \
  150. } else { \
  151. auto const d = m_data.num; \
  152. auto const t = m_type; \
  153. setOp; \
  154. tvDecRefHelper(t, d); \
  155. } \
  156. }
  157. #define IMPLEMENT_VOID_SET(name, setOp) \
  158. IMPLEMENT_SET_IMPL(name, , , setOp)
  159. #define IMPLEMENT_SET(argType, setOp) \
  160. IMPLEMENT_SET_IMPL(set, argType, v, setOp)
  161. IMPLEMENT_VOID_SET(setNull, m_type = KindOfNull)
  162. IMPLEMENT_SET(bool, m_type = KindOfBoolean; m_data.num = v)
  163. IMPLEMENT_SET(int, m_type = KindOfInt64; m_data.num = v)
  164. IMPLEMENT_SET(int64_t, m_type = KindOfInt64; m_data.num = v)
  165. IMPLEMENT_SET(double, m_type = KindOfDouble; m_data.dbl = v)
  166. IMPLEMENT_SET(const StaticString&,
  167. StringData* s = v.get();
  168. assert(s);
  169. m_type = KindOfStaticString;
  170. m_data.pstr = s)
  171. #undef IMPLEMENT_SET_IMPL
  172. #undef IMPLEMENT_VOID_SET
  173. #undef IMPLEMENT_SET
  174. #define IMPLEMENT_PTR_SET(ptr, member, dtype) \
  175. void Variant::set(ptr *v) noexcept { \
  176. Variant *self = m_type == KindOfRef ? m_data.pref->var() : this; \
  177. if (UNLIKELY(!v)) { \
  178. self->setNull(); \
  179. } else { \
  180. v->incRefCount(); \
  181. auto const d = self->m_data.num; \
  182. auto const t = self->m_type; \
  183. self->m_type = dtype; \
  184. self->m_data.member = v; \
  185. tvRefcountedDecRefHelper(t, d); \
  186. } \
  187. }
  188. IMPLEMENT_PTR_SET(StringData, pstr,
  189. v->isStatic() ? KindOfStaticString : KindOfString);
  190. IMPLEMENT_PTR_SET(ArrayData, parr, KindOfArray)
  191. IMPLEMENT_PTR_SET(ObjectData, pobj, KindOfObject)
  192. IMPLEMENT_PTR_SET(ResourceData, pres, KindOfResource)
  193. #undef IMPLEMENT_PTR_SET
  194. #define IMPLEMENT_STEAL(ptr, member, dtype) \
  195. void Variant::steal(ptr* v) noexcept { \
  196. Variant* self = (m_type == KindOfRef) ? m_data.pref->var() : this; \
  197. if (UNLIKELY(!v)) { \
  198. self->setNull(); \
  199. } else { \
  200. auto const d = self->m_data.num; \
  201. auto const t = self->m_type; \
  202. self->m_type = dtype; \
  203. self->m_data.member = v; \
  204. tvRefcountedDecRefHelper(t, d); \
  205. } \
  206. }
  207. IMPLEMENT_STEAL(StringData, pstr,
  208. v->isStatic() ? KindOfStaticString : KindOfString)
  209. IMPLEMENT_STEAL(ArrayData, parr, KindOfArray)
  210. IMPLEMENT_STEAL(ObjectData, pobj, KindOfObject)
  211. IMPLEMENT_STEAL(ResourceData, pres, KindOfResource)
  212. #undef IMPLEMENT_STEAL
  213. int Variant::getRefCount() const noexcept {
  214. switch (m_type) {
  215. DT_UNCOUNTED_CASE:
  216. return 1;
  217. case KindOfString: return m_data.pstr->getCount();
  218. case KindOfArray: return m_data.parr->getCount();
  219. case KindOfObject: return m_data.pobj->getCount();
  220. case KindOfResource: return m_data.pres->getCount();
  221. case KindOfRef: return m_data.pref->var()->getRefCount();
  222. case KindOfClass: break;
  223. }
  224. not_reached();
  225. }
  226. ///////////////////////////////////////////////////////////////////////////////
  227. // informational
  228. bool Variant::isNumeric(bool checkString /* = false */) const noexcept {
  229. int64_t ival;
  230. double dval;
  231. DataType t = toNumeric(ival, dval, checkString);
  232. return t == KindOfInt64 || t == KindOfDouble;
  233. }
  234. DataType Variant::toNumeric(int64_t &ival, double &dval,
  235. bool checkString /* = false */) const {
  236. switch (m_type) {
  237. case KindOfUninit:
  238. case KindOfNull:
  239. case KindOfBoolean:
  240. case KindOfArray:
  241. case KindOfObject:
  242. case KindOfResource:
  243. return m_type;
  244. case KindOfInt64:
  245. ival = m_data.num;
  246. return KindOfInt64;
  247. case KindOfDouble:
  248. dval = m_data.dbl;
  249. return KindOfDouble;
  250. case KindOfStaticString:
  251. case KindOfString:
  252. return checkString ? m_data.pstr->toNumeric(ival, dval) : m_type;
  253. case KindOfRef:
  254. return m_data.pref->var()->toNumeric(ival, dval, checkString);
  255. case KindOfClass:
  256. break;
  257. }
  258. not_reached();
  259. }
  260. bool Variant::isScalar() const noexcept {
  261. switch (getType()) {
  262. case KindOfUninit:
  263. case KindOfNull:
  264. case KindOfArray:
  265. case KindOfObject:
  266. case KindOfResource:
  267. return false;
  268. case KindOfBoolean:
  269. case KindOfInt64:
  270. case KindOfDouble:
  271. case KindOfStaticString:
  272. case KindOfString:
  273. return true;
  274. case KindOfRef:
  275. always_assert(false && "isScalar() called on a boxed value");
  276. case KindOfClass:
  277. break;
  278. }
  279. not_reached();
  280. }
  281. ///////////////////////////////////////////////////////////////////////////////
  282. inline DataType Variant::convertToNumeric(int64_t *lval, double *dval) const {
  283. StringData *s = getStringData();
  284. assert(s);
  285. return s->isNumericWithVal(*lval, *dval, 1);
  286. }
  287. ///////////////////////////////////////////////////////////////////////////////
  288. // type conversions
  289. bool Variant::toBooleanHelper() const {
  290. assert(m_type > KindOfInt64);
  291. switch (m_type) {
  292. case KindOfUninit:
  293. case KindOfNull:
  294. case KindOfBoolean:
  295. case KindOfInt64: return m_data.num;
  296. case KindOfDouble: return m_data.dbl != 0;
  297. case KindOfStaticString:
  298. case KindOfString: return m_data.pstr->toBoolean();
  299. case KindOfArray: return !m_data.parr->empty();
  300. case KindOfObject: return m_data.pobj->toBoolean();
  301. case KindOfResource: return m_data.pres->o_toBoolean();
  302. case KindOfRef: return m_data.pref->var()->toBoolean();
  303. case KindOfClass: break;
  304. }
  305. not_reached();
  306. }
  307. int64_t Variant::toInt64Helper(int base /* = 10 */) const {
  308. assert(m_type > KindOfInt64);
  309. switch (m_type) {
  310. case KindOfUninit:
  311. case KindOfNull:
  312. case KindOfBoolean:
  313. case KindOfInt64: return m_data.num;
  314. case KindOfDouble: return HPHP::toInt64(m_data.dbl);
  315. case KindOfStaticString:
  316. case KindOfString: return m_data.pstr->toInt64(base);
  317. case KindOfArray: return m_data.parr->empty() ? 0 : 1;
  318. case KindOfObject: return m_data.pobj->toInt64();
  319. case KindOfResource: return m_data.pres->o_toInt64();
  320. case KindOfRef: return m_data.pref->var()->toInt64(base);
  321. case KindOfClass: break;
  322. }
  323. not_reached();
  324. }
  325. double Variant::toDoubleHelper() const {
  326. switch (m_type) {
  327. case KindOfUninit:
  328. case KindOfNull: return 0.0;
  329. case KindOfBoolean:
  330. case KindOfInt64: return (double)toInt64();
  331. case KindOfDouble: return m_data.dbl;
  332. case KindOfStaticString:
  333. case KindOfString: return m_data.pstr->toDouble();
  334. case KindOfArray: return (double)toInt64();
  335. case KindOfObject: return m_data.pobj->toDouble();
  336. case KindOfResource: return m_data.pres->o_toDouble();
  337. case KindOfRef: return m_data.pref->var()->toDouble();
  338. case KindOfClass: break;
  339. }
  340. not_reached();
  341. }
  342. String Variant::toStringHelper() const {
  343. switch (m_type) {
  344. case KindOfUninit:
  345. case KindOfNull:
  346. return empty_string();
  347. case KindOfBoolean:
  348. return m_data.num ? static_cast<String>(s_1)
  349. : empty_string();
  350. case KindOfInt64:
  351. return m_data.num;
  352. case KindOfDouble:
  353. return m_data.dbl;
  354. case KindOfStaticString:
  355. case KindOfString:
  356. assert(false); // Should be done in caller
  357. return m_data.pstr;
  358. case KindOfArray:
  359. raise_notice("Array to string conversion");
  360. return array_string;
  361. case KindOfObject:
  362. return m_data.pobj->invokeToString();
  363. case KindOfResource:
  364. return m_data.pres->o_toString();
  365. case KindOfRef:
  366. return m_data.pref->var()->toString();
  367. case KindOfClass:
  368. break;
  369. }
  370. not_reached();
  371. }
  372. Array Variant::toArrayHelper() const {
  373. switch (m_type) {
  374. case KindOfUninit:
  375. case KindOfNull: return empty_array();
  376. case KindOfBoolean: return Array::Create(*this);
  377. case KindOfInt64: return Array::Create(m_data.num);
  378. case KindOfDouble: return Array::Create(*this);
  379. case KindOfStaticString:
  380. case KindOfString: return Array::Create(m_data.pstr);
  381. case KindOfArray: return Array(m_data.parr);
  382. case KindOfObject: return m_data.pobj->toArray();
  383. case KindOfResource: return m_data.pres->o_toArray();
  384. case KindOfRef: return m_data.pref->var()->toArray();
  385. case KindOfClass: break;
  386. }
  387. not_reached();
  388. }
  389. Object Variant::toObjectHelper() const {
  390. switch (m_type) {
  391. case KindOfUninit:
  392. case KindOfNull:
  393. return SystemLib::AllocStdClassObject();
  394. case KindOfBoolean:
  395. case KindOfInt64:
  396. case KindOfDouble:
  397. case KindOfStaticString:
  398. case KindOfString:
  399. case KindOfResource: {
  400. auto obj = SystemLib::AllocStdClassObject();
  401. obj->o_set(s_scalar, *this, false);
  402. return obj;
  403. }
  404. case KindOfArray:
  405. return ObjectData::FromArray(m_data.parr);
  406. case KindOfObject:
  407. return m_data.pobj;
  408. case KindOfRef:
  409. return m_data.pref->var()->toObject();
  410. case KindOfClass:
  411. break;
  412. }
  413. not_reached();
  414. }
  415. Resource Variant::toResourceHelper() const {
  416. switch (m_type) {
  417. case KindOfUninit:
  418. case KindOfNull:
  419. case KindOfBoolean:
  420. case KindOfInt64:
  421. case KindOfDouble:
  422. case KindOfStaticString:
  423. case KindOfString:
  424. case KindOfArray:
  425. case KindOfObject:
  426. return Resource(makeSmartPtr<DummyResource>());
  427. case KindOfResource:
  428. return m_data.pres;
  429. case KindOfRef:
  430. return m_data.pref->var()->toResource();
  431. case KindOfClass:
  432. break;
  433. }
  434. not_reached();
  435. }
  436. VarNR Variant::toKey() const {
  437. if (m_type == KindOfString || m_type == KindOfStaticString) {
  438. int64_t n;
  439. if (m_data.pstr->isStrictlyInteger(n)) {
  440. return VarNR(n);
  441. } else {
  442. return VarNR(m_data.pstr);
  443. }
  444. }
  445. switch (m_type) {
  446. case KindOfUninit:
  447. case KindOfNull:
  448. return VarNR(staticEmptyString());
  449. case KindOfBoolean:
  450. case KindOfInt64:
  451. return VarNR(m_data.num);
  452. case KindOfDouble:
  453. case KindOfResource:
  454. return VarNR(toInt64());
  455. case KindOfStaticString:
  456. case KindOfString:
  457. case KindOfArray:
  458. case KindOfObject:
  459. throw_bad_type_exception("Invalid type used as key");
  460. return null_varNR;
  461. case KindOfRef:
  462. return m_data.pref->var()->toKey();
  463. case KindOfClass:
  464. break;
  465. }
  466. not_reached();
  467. }
  468. ///////////////////////////////////////////////////////////////////////////////
  469. // offset functions
  470. template <typename T>
  471. class LvalHelper {};
  472. template<>
  473. class LvalHelper<int64_t> {
  474. public:
  475. typedef int64_t KeyType;
  476. static bool CheckKey(KeyType k) { return true; };
  477. static const bool CheckParams = false;
  478. };
  479. template<>
  480. class LvalHelper<bool> : public LvalHelper<int64_t> {};
  481. template<>
  482. class LvalHelper<double> : public LvalHelper<int64_t> {};
  483. template<>
  484. class LvalHelper<const String&> {
  485. public:
  486. typedef VarNR KeyType;
  487. static bool CheckKey(const KeyType &k) { return true; };
  488. static const bool CheckParams = true;
  489. };
  490. template<>
  491. class LvalHelper<const Variant&> {
  492. public:
  493. typedef VarNR KeyType;
  494. static bool CheckKey(const KeyType &k) { return !k.isNull(); };
  495. static const bool CheckParams = true;
  496. };
  497. Variant& lvalBlackHole() {
  498. auto& bh = get_env_constants()->lvalProxy;
  499. bh = uninit_null();
  500. return bh;
  501. }
  502. void Variant::setEvalScalar() {
  503. switch (m_type) {
  504. DT_UNCOUNTED_CASE:
  505. return;
  506. case KindOfString: {
  507. StringData *pstr = m_data.pstr;
  508. if (!pstr->isStatic()) {
  509. StringData *sd = makeStaticString(pstr);
  510. decRefStr(pstr);
  511. m_data.pstr = sd;
  512. assert(m_data.pstr->isStatic());
  513. m_type = KindOfStaticString;
  514. }
  515. return;
  516. }
  517. case KindOfArray: {
  518. ArrayData *parr = m_data.parr;
  519. if (!parr->isStatic()) {
  520. ArrayData *ad = ArrayData::GetScalarArray(parr);
  521. decRefArr(parr);
  522. m_data.parr = ad;
  523. assert(m_data.parr->isStatic());
  524. }
  525. return;
  526. }
  527. case KindOfObject:
  528. case KindOfResource:
  529. case KindOfRef:
  530. case KindOfClass:
  531. break;
  532. }
  533. not_reached();
  534. }
  535. namespace {
  536. static void serializeRef(const TypedValue* tv,
  537. VariableSerializer* serializer,
  538. bool isArrayKey) {
  539. assert(tv->m_type == KindOfRef);
  540. // Ugly, but behavior is different for serialize
  541. if (serializer->getType() == VariableSerializer::Type::Serialize ||
  542. serializer->getType() == VariableSerializer::Type::APCSerialize ||
  543. serializer->getType() == VariableSerializer::Type::DebuggerSerialize) {
  544. if (serializer->incNestedLevel(tv->m_data.pref->var())) {
  545. serializer->writeOverflow(tv->m_data.pref->var());
  546. } else {
  547. // Tell the inner variant to skip the nesting check for data inside
  548. serializeVariant(*tv->m_data.pref->var(), serializer, isArrayKey, true);
  549. }
  550. serializer->decNestedLevel(tv->m_data.pref->var());
  551. } else {
  552. serializeVariant(*tv->m_data.pref->var(), serializer, isArrayKey);
  553. }
  554. }
  555. }
  556. ///////////////////////////////////////////////////////////////////////////////
  557. // output functions
  558. void serializeVariant(const Variant& self, VariableSerializer *serializer,
  559. bool isArrayKey /* = false */,
  560. bool skipNestCheck /* = false */,
  561. bool noQuotes /* = false */) {
  562. auto tv = self.asTypedValue();
  563. switch (tv->m_type) {
  564. case KindOfUninit:
  565. case KindOfNull:
  566. assert(!isArrayKey);
  567. serializer->writeNull();
  568. return;
  569. case KindOfBoolean:
  570. assert(!isArrayKey);
  571. serializer->write(tv->m_data.num != 0);
  572. return;
  573. case KindOfInt64:
  574. serializer->write(tv->m_data.num);
  575. return;
  576. case KindOfDouble:
  577. serializer->write(tv->m_data.dbl);
  578. return;
  579. case KindOfStaticString:
  580. case KindOfString:
  581. serializer->write(tv->m_data.pstr->data(),
  582. tv->m_data.pstr->size(), isArrayKey, noQuotes);
  583. return;
  584. case KindOfArray:
  585. assert(!isArrayKey);
  586. tv->m_data.parr->serialize(serializer, skipNestCheck);
  587. return;
  588. case KindOfObject:
  589. assert(!isArrayKey);
  590. tv->m_data.pobj->serialize(serializer);
  591. return;
  592. case KindOfResource:
  593. assert(!isArrayKey);
  594. tv->m_data.pres->serialize(serializer);
  595. return;
  596. case KindOfRef:
  597. serializeRef(tv, serializer, isArrayKey);
  598. return;
  599. case KindOfClass:
  600. break;
  601. }
  602. not_reached();
  603. }
  604. static void unserializeProp(VariableUnserializer* uns,
  605. ObjectData* obj,
  606. const String& key,
  607. Class* ctx,
  608. const String& realKey,
  609. int nProp) {
  610. // Do a two-step look up
  611. auto const lookup = obj->getProp(ctx, key.get());
  612. Variant* t;
  613. if (!lookup.prop || !lookup.accessible) {
  614. // Dynamic property. If this is the first, and we're using MixedArray,
  615. // we need to pre-allocate space in the array to ensure the elements
  616. // dont move during unserialization.
  617. //
  618. // TODO(#2881866): this assumption means we can't do reallocations
  619. // when promoting kPackedKind -> kMixedKind.
  620. t = &obj->reserveProperties(nProp).lvalAt(realKey, AccessFlags::Key);
  621. } else {
  622. t = &tvAsVariant(lookup.prop);
  623. }
  624. if (UNLIKELY(IS_REFCOUNTED_TYPE(t->getRawType()))) {
  625. uns->putInOverwrittenList(*t);
  626. }
  627. unserializeVariant(*t, uns);
  628. if (!RuntimeOption::RepoAuthoritative) return;
  629. if (!Repo::get().global().HardPrivatePropInference) return;
  630. /*
  631. * We assume for performance reasons in repo authoriative mode that
  632. * we can see all the sets to private properties in a class.
  633. *
  634. * It's a hole in this if we don't check unserialization doesn't
  635. * violate what we've seen, which we handle by throwing if the repo
  636. * was built with this option.
  637. */
  638. auto const cls = obj->getVMClass();
  639. auto const slot = cls->lookupDeclProp(key.get());
  640. if (UNLIKELY(slot == kInvalidSlot)) return;
  641. auto const repoTy = obj->getVMClass()->declPropRepoAuthType(slot);
  642. if (LIKELY(tvMatchesRepoAuthType(*t->asTypedValue(), repoTy))) {
  643. return;
  644. }
  645. auto msg = folly::format(
  646. "Property {} for class {} was deserialized with type ({}) that "
  647. "didn't match what we inferred in static analysis",
  648. key.data(),
  649. obj->getVMClass()->name()->data(),
  650. tname(t->asTypedValue()->m_type)
  651. ).str();
  652. throw Exception(msg);
  653. }
  654. /*
  655. * For namespaced collections, returns an "alternate" name, which is a
  656. * collection name with or without the namespace qualifier, depending on
  657. * what's passed.
  658. * If no alternate name is found, returns nullptr.
  659. */
  660. static const StringData* getAlternateCollectionName(const StringData* clsName) {
  661. typedef hphp_hash_map<const StringData*, const StringData*,
  662. string_data_hash, string_data_isame> ClsNameMap;
  663. auto getAltMap = [] {
  664. typedef std::pair<StaticString, StaticString> SStringPair;
  665. static ClsNameMap m;
  666. static std::vector<SStringPair> mappings {
  667. std::make_pair(StaticString("Vector"), StaticString("HH\\Vector")),
  668. std::make_pair(StaticString("Map"), StaticString("HH\\Map")),
  669. std::make_pair(StaticString("Set"), StaticString("HH\\Set")),
  670. std::make_pair(StaticString("Pair"), StaticString("HH\\Pair"))
  671. };
  672. for (const auto& p : mappings) {
  673. m[p.first.get()] = p.second.get();
  674. m[p.second.get()] = p.first.get();
  675. }
  676. // As part of StableMap merging into Map, StableMap is an alias for HH\\Map,
  677. // but Map is the sole alias for HH\\Map
  678. m[StaticString("StableMap").get()] = StaticString("HH\\Map").get();
  679. return &m;
  680. };
  681. static const ClsNameMap* altMap = getAltMap();
  682. auto it = altMap->find(clsName);
  683. return it != altMap->end() ? it->second : nullptr;
  684. }
  685. static Class* tryAlternateCollectionClass(const StringData* clsName) {
  686. auto altName = getAlternateCollectionName(clsName);
  687. return altName ? Unit::getClass(altName, /* autoload */ false) : nullptr;
  688. }
  689. void unserializeVariant(Variant& self, VariableUnserializer *uns,
  690. UnserializeMode mode /* = UnserializeMode::Value */) {
  691. // NOTE: If you make changes to how serialization and unserialization work,
  692. // make sure to update the reserialize() method in "runtime/ext/ext_apc.cpp"
  693. // and to update test_apc_reserialize() in "test/ext/test_ext_apc.cpp".
  694. char type = uns->readChar();
  695. char sep = uns->readChar();
  696. if (type != 'R') {
  697. uns->add(&self, mode);
  698. }
  699. if (type == 'N') {
  700. if (sep != ';') throw Exception("Expected ';' but got '%c'", sep);
  701. self.setNull(); // NULL *IS* the value, without we get undefined warnings
  702. return;
  703. }
  704. if (sep != ':') {
  705. throw Exception("Expected ':' but got '%c'", sep);
  706. }
  707. switch (type) {
  708. case 'r':
  709. {
  710. int64_t id = uns->readInt();
  711. Variant *v = uns->getByVal(id);
  712. if (v == nullptr) {
  713. throw Exception("Id %" PRId64 " out of range", id);
  714. }
  715. self = *v;
  716. }
  717. break;
  718. case 'R':
  719. {
  720. int64_t id = uns->readInt();
  721. Variant *v = uns->getByRef(id);
  722. if (v == nullptr) {
  723. throw Exception("Id %" PRId64 " out of range", id);
  724. }
  725. self.assignRef(*v);
  726. }
  727. break;
  728. case 'b': { int64_t v = uns->readInt(); self = (bool)v; } break;
  729. case 'i': { int64_t v = uns->readInt(); self = v; } break;
  730. case 'd':
  731. {
  732. double v;
  733. char ch = uns->peek();
  734. bool negative = false;
  735. char buf[4];
  736. if (ch == '-') {
  737. negative = true;
  738. ch = uns->readChar();
  739. ch = uns->peek();
  740. }
  741. if (ch == 'I') {
  742. uns->read(buf, 3); buf[3] = '\0';
  743. if (strcmp(buf, "INF")) {
  744. throw Exception("Expected 'INF' but got '%s'", buf);
  745. }
  746. v = atof("inf");
  747. } else if (ch == 'N') {
  748. uns->read(buf, 3); buf[3] = '\0';
  749. if (strcmp(buf, "NAN")) {
  750. throw Exception("Expected 'NAN' but got '%s'", buf);
  751. }
  752. v = atof("nan");
  753. } else {
  754. v = uns->readDouble();
  755. }
  756. self = negative ? -v : v;
  757. }
  758. break;
  759. case 's':
  760. {
  761. String v;
  762. v.unserialize(uns);
  763. self = std::move(v);
  764. if (!uns->endOfBuffer()) {
  765. // Semicolon *should* always be required,
  766. // but PHP's implementation allows omitting it
  767. // and still functioning.
  768. // Worse, it throws it away without any check.
  769. // So we'll do the same. Sigh.
  770. uns->readChar();
  771. }
  772. }
  773. return;
  774. case 'S':
  775. if (uns->type() == VariableUnserializer::Type::APCSerialize) {
  776. union {
  777. char buf[8];
  778. StringData *sd;
  779. } u;
  780. uns->read(u.buf, 8);
  781. self = u.sd;
  782. } else {
  783. throw Exception("Unknown type '%c'", type);
  784. }
  785. break;
  786. case 'a':
  787. {
  788. // Check stack depth to avoid overflow.
  789. check_recursion_throw();
  790. auto v = Array::Create();
  791. v.unserialize(uns);
  792. self = std::move(v);
  793. }
  794. return; // array has '}' terminating
  795. case 'L':
  796. {
  797. int64_t id = uns->readInt();
  798. uns->expectChar(':');
  799. String rsrcName;
  800. rsrcName.unserialize(uns);
  801. uns->expectChar('{');
  802. uns->expectChar('}');
  803. auto rsrc = makeSmartPtr<DummyResource>();
  804. rsrc->o_setResourceId(id);
  805. rsrc->m_class_name = rsrcName;
  806. self = std::move(rsrc);
  807. }
  808. return; // resource has '}' terminating
  809. case 'O':
  810. case 'V':
  811. case 'K':
  812. {
  813. String clsName;
  814. clsName.unserialize(uns);
  815. uns->expectChar(':');
  816. int64_t size = uns->readInt();
  817. uns->expectChar(':');
  818. uns->expectChar('{');
  819. const bool allowObjectFormatForCollections = true;
  820. Class* cls;
  821. // If we are potentially dealing with a collection, we need to try to
  822. // load the collection class under an alternate name so that we can
  823. // deserialize data that was serialized before the migration of
  824. // collections to the HH namespace.
  825. if (type != 'O') {
  826. // Collections are CPP builtins; don't attempt to autoload
  827. cls = Unit::getClass(clsName.get(), /* autoload */ false);
  828. if (!cls) {
  829. cls = tryAlternateCollectionClass(clsName.get());
  830. }
  831. } else if (allowObjectFormatForCollections) {
  832. // In order to support the legacy {O|V}:{Set|Vector|Map}
  833. // serialization, we defer autoloading until we know that there's
  834. // no alternate (builtin) collection class.
  835. cls = Unit::getClass(clsName.get(), /* autoload */ false);
  836. if (!cls) {
  837. cls = tryAlternateCollectionClass(clsName.get());
  838. }
  839. if (!cls) {
  840. cls = Unit::loadClass(clsName.get()); // with autoloading
  841. }
  842. } else {
  843. cls = Unit::loadClass(clsName.get()); // with autoloading
  844. }
  845. Object obj;
  846. if (RuntimeOption::UnserializationWhitelistCheck &&
  847. (type == 'O') &&
  848. !uns->isWhitelistedClass(clsName)) {
  849. const char* err_msg =
  850. "The object being unserialized with class name '%s' "
  851. "is not in the given whitelist. "
  852. "See http://fburl.com/SafeSerializable for more detail";
  853. if (RuntimeOption::UnserializationWhitelistCheckWarningOnly) {
  854. raise_warning(err_msg, clsName.c_str());
  855. } else {
  856. raise_error(err_msg, clsName.c_str());
  857. }
  858. }
  859. if (cls) {
  860. // Only unserialize CPP extension types which can actually
  861. // support it. Otherwise, we risk creating a CPP object
  862. // without having it initialized completely.
  863. if (cls->instanceCtor() && !cls->isCppSerializable()) {
  864. assert(obj.isNull());
  865. throw_null_pointer_exception();
  866. } else {
  867. obj = Object{cls};
  868. if (UNLIKELY(collections::isType(cls, CollectionType::Pair) &&
  869. (size != 2))) {
  870. throw Exception("Pair objects must have exactly 2 elements");
  871. }
  872. }
  873. } else {
  874. obj = Object{SystemLib::s___PHP_Incomplete_ClassClass};
  875. obj->o_set(s_PHP_Incomplete_Class_Name, clsName);
  876. }
  877. assert(!obj.isNull());
  878. self = obj;
  879. if (size > 0) {
  880. // Check stack depth to avoid overflow.
  881. check_recursion_throw();
  882. if (type == 'O') {
  883. // Collections are not allowed
  884. if (obj->isCollection()) {
  885. throw Exception("%s does not support the 'O' serialization "
  886. "format", clsName.data());
  887. }
  888. Variant serializedNativeData = init_null();
  889. bool hasSerializedNativeData = false;
  890. /*
  891. Count backwards so that i is the number of properties
  892. remaining (to be used as an estimate for the total number
  893. of dynamic properties when we see the first dynamic prop).
  894. see getVariantPtr
  895. */
  896. for (int64_t i = size; i--; ) {
  897. Variant v;
  898. unserializeVariant(v, uns, UnserializeMode::Key);
  899. String key = v.toString();
  900. int ksize = key.size();
  901. const char *kdata = key.data();
  902. int subLen = 0;
  903. if (key == ObjectData::s_serializedNativeDataKey) {
  904. unserializeVariant(serializedNativeData, uns);
  905. hasSerializedNativeData = true;
  906. } else if (kdata[0] == '\0') {
  907. if (UNLIKELY(!ksize)) {
  908. raise_error("Cannot access empty property");
  909. }
  910. // private or protected
  911. subLen = strlen(kdata + 1) + 2;
  912. if (UNLIKELY(subLen >= ksize)) {
  913. if (subLen == ksize) {
  914. raise_error("Cannot access empty property");
  915. } else {
  916. throw Exception("Mangled private object property");
  917. }
  918. }
  919. String k(kdata + subLen, ksize - subLen, CopyString);
  920. Class* ctx = (Class*)-1;
  921. if (kdata[1] != '*') {
  922. ctx = Unit::lookupClass(
  923. String(kdata + 1, subLen - 2, CopyString).get());
  924. }
  925. unserializeProp(uns, obj.get(), k, ctx, key, i + 1);
  926. } else {
  927. unserializeProp(uns, obj.get(), key, nullptr, key, i + 1);
  928. }
  929. if (i > 0) {
  930. auto lastChar = uns->peekBack();
  931. if ((lastChar != ';') && (lastChar != '}')) {
  932. throw Exception("Object property not terminated properly");
  933. }
  934. }
  935. }
  936. // nativeDataWakeup is called last to ensure that all properties are
  937. // already unserialized. We also ensure that nativeDataWakeup is
  938. // invoked regardless of whether or not serialized native data exists
  939. // within the serialized content.
  940. if (obj->getAttribute(ObjectData::HasNativeData) &&
  941. obj->getVMClass()->getNativeDataInfo()->isSerializable()) {
  942. Native::nativeDataWakeup(obj.get(), serializedNativeData);
  943. } else if (hasSerializedNativeData) {
  944. raise_warning("%s does not expect any serialized native data.",
  945. clsName.data());
  946. }
  947. } else {
  948. assert(type == 'V' || type == 'K');
  949. if (!obj->isCollection()) {
  950. throw Exception("%s is not a collection class", clsName.data());
  951. }
  952. collections::unserialize(obj.get(), uns, size, type);
  953. }
  954. }
  955. uns->expectChar('}');
  956. if (uns->type() != VariableUnserializer::Type::DebuggerSerialize ||
  957. (cls && cls->instanceCtor() && cls->isCppSerializable())) {
  958. // Don't call wakeup when unserializing for the debugger, except for
  959. // natively implemented classes.
  960. obj->invokeWakeup();
  961. }
  962. check_request_surprise_unlikely();
  963. }
  964. return; // object has '}' terminating
  965. case 'C':
  966. {
  967. if (uns->type() == VariableUnserializer::Type::DebuggerSerialize) {
  968. raise_error("Debugger shouldn't call custom unserialize method");
  969. }
  970. String clsName;
  971. clsName.unserialize(uns);
  972. uns->expectChar(':');
  973. String serialized;
  974. serialized.unserialize(uns, '{', '}');
  975. auto const obj = [&]() -> Object {
  976. if (auto const cls = Unit::loadClass(clsName.get())) {
  977. return Object::attach(g_context->createObject(cls, init_null_variant,
  978. false /* init */));
  979. }
  980. if (!uns->allowUnknownSerializableClass()) {
  981. raise_error("unknown class %s", clsName.data());
  982. }
  983. Object ret = create_object_only(s_PHP_Incomplete_Class);
  984. ret->o_set(s_PHP_Incomplete_Class_Name, clsName);
  985. ret->o_set("serialized", serialized);
  986. return ret;
  987. }();
  988. if (!obj->instanceof(SystemLib::s_SerializableClass)) {
  989. raise_warning("Class %s has no unserializer",
  990. obj->getClassName().data());
  991. } else {
  992. obj->o_invoke_few_args(s_unserialize, 1, serialized);
  993. obj.get()->clearNoDestruct();
  994. }
  995. self = std::move(obj);
  996. }
  997. return; // object has '}' terminating
  998. default:
  999. throw Exception("Unknown type '%c'", type);
  1000. }
  1001. uns->expectChar(';');
  1002. }
  1003. VarNR::VarNR(const String& v) {
  1004. init(KindOfString);
  1005. StringData *s = v.get();
  1006. if (s) {
  1007. m_data.pstr = s;
  1008. } else {
  1009. m_type = KindOfNull;
  1010. }
  1011. }
  1012. VarNR::VarNR(const Array& v) {
  1013. init(KindOfArray);
  1014. ArrayData *a = v.get();
  1015. if (a) {
  1016. m_data.parr = a;
  1017. } else {
  1018. m_type = KindOfNull;
  1019. }
  1020. }
  1021. VarNR::VarNR(const Object& v) {
  1022. init(KindOfObject);
  1023. ObjectData *o = v.get();
  1024. if (o) {
  1025. m_data.pobj = o;
  1026. } else {
  1027. m_type = KindOfNull;
  1028. }
  1029. }
  1030. VarNR::VarNR(StringData *v) {
  1031. init(KindOfString);
  1032. if (v) {
  1033. m_data.pstr = v;
  1034. } else {
  1035. m_type = KindOfNull;
  1036. }
  1037. }
  1038. VarNR::VarNR(ArrayData *v) {
  1039. init(KindOfArray);
  1040. if (v) {
  1041. m_data.parr = v;
  1042. } else {
  1043. m_type = KindOfNull;
  1044. }
  1045. }
  1046. VarNR::VarNR(ObjectData *v) {
  1047. init(KindOfObject);
  1048. if (v) {
  1049. m_data.pobj = v;
  1050. } else {
  1051. m_type = KindOfNull;
  1052. }
  1053. }
  1054. ///////////////////////////////////////////////////////////////////////////////
  1055. }