6e7b02933c
The 'r' encoding for unserialization was broken for collections because the code was calling Variant::unserialize() on a temporary Variant, which is a no-no. unserialize() must be called directly on the value where it resides in the collection. Second, there was an inconsistency between serialize and unserialize with how id numbers worked for the 'r' and 'R' encodings. This diff fixes serialize and unserialize to count collection keys when assigning id numbers. I also took the opportunity to tighten up enforcement to prevent collections keys and values from being taken by reference when during unserialization.
138 linhas
4.4 KiB
C++
138 linhas
4.4 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifndef __HPHP_VARIABLE_UNSERIALIZER_H__
|
|
#define __HPHP_VARIABLE_UNSERIALIZER_H__
|
|
|
|
#include <runtime/base/types.h>
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
class VariableUnserializer {
|
|
public:
|
|
/**
|
|
* Supported formats.
|
|
*/
|
|
enum Type {
|
|
Serialize,
|
|
APCSerialize,
|
|
};
|
|
|
|
public:
|
|
VariableUnserializer(const char *str, size_t len, Type type,
|
|
bool allowUnknownSerializableClass = false)
|
|
: m_type(type), m_buf(str), m_end(str + len),
|
|
m_unknownSerializable(allowUnknownSerializableClass) {}
|
|
VariableUnserializer(const char *str, const char *end, Type type,
|
|
bool allowUnknownSerializableClass = false)
|
|
: m_type(type), m_buf(str), m_end(end),
|
|
m_unknownSerializable(allowUnknownSerializableClass) {}
|
|
|
|
Type getType() const { return m_type;}
|
|
bool allowUnknownSerializableClass() const { return m_unknownSerializable;}
|
|
|
|
Variant unserialize();
|
|
Variant unserializeKey();
|
|
void add(Variant* v, Uns::Mode mode) {
|
|
if (mode == Uns::ValueMode) {
|
|
m_refs.emplace_back(RefInfo(v));
|
|
} else if (mode == Uns::KeyMode) {
|
|
// do nothing
|
|
} else if (mode == Uns::ColValueMode) {
|
|
m_refs.emplace_back(RefInfo::makeNonRefable(v));
|
|
} else {
|
|
assert(mode == Uns::ColKeyMode);
|
|
// We don't currently support using the 'r' encoding to refer
|
|
// to collection keys, but eventually we'll need to make this
|
|
// work to allow objects as keys. For now we encode collections
|
|
// keys in m_refs using a null pointer.
|
|
m_refs.emplace_back(RefInfo(nullptr));
|
|
}
|
|
}
|
|
// getByVal() is used to resolve the 'r' encoding
|
|
Variant* getByVal(int id) {
|
|
if (id <= 0 || id > (int)m_refs.size()) return nullptr;
|
|
Variant* ret = m_refs[id-1].var();
|
|
if (!ret) {
|
|
throw Exception("Referring to collection keys using the 'r' encoding "
|
|
"is not supported");
|
|
}
|
|
return ret;
|
|
}
|
|
// getByVal() is used to resolve the 'R' encoding
|
|
Variant* getByRef(int id) {
|
|
if (id <= 0 || id > (int)m_refs.size()) return nullptr;
|
|
if (!m_refs[id-1].canBeReferenced()) {
|
|
// If the low bit is set, that means the value cannot
|
|
// be taken by reference
|
|
throw Exception("Collection values cannot be taken by reference", id);
|
|
}
|
|
Variant* ret = m_refs[id-1].var();
|
|
if (!ret) {
|
|
throw Exception("Collection keys cannot be taken by reference", id);
|
|
}
|
|
return ret;
|
|
}
|
|
int64_t readInt();
|
|
double readDouble();
|
|
char readChar() {
|
|
check();
|
|
return *(m_buf++);
|
|
}
|
|
void read(char *buf, uint n);
|
|
char peek() {
|
|
check();
|
|
return *m_buf;
|
|
}
|
|
const char *head() { return m_buf; }
|
|
Variant &addVar();
|
|
|
|
private:
|
|
struct RefInfo {
|
|
explicit RefInfo(Variant* v) : m_data(reinterpret_cast<uintptr_t>(v)) {}
|
|
static RefInfo makeNonRefable(Variant* v) {
|
|
RefInfo r(v);
|
|
r.m_data |= 1;
|
|
return r;
|
|
}
|
|
Variant* var() const {
|
|
return reinterpret_cast<Variant*>(m_data & ~1);
|
|
}
|
|
bool canBeReferenced() const { return !(m_data & 1); }
|
|
private:
|
|
uintptr_t m_data;
|
|
};
|
|
|
|
Type m_type;
|
|
const char *m_buf;
|
|
const char *m_end;
|
|
std::vector<RefInfo> m_refs;
|
|
std::list<Variant> m_vars;
|
|
bool m_unknownSerializable;
|
|
|
|
void check() {
|
|
if (m_buf >= m_end) {
|
|
throw Exception("Unexpected end of buffer during unserialization");
|
|
}
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
#endif // __HPHP_VARIABLE_UNSERIALIZER_H__
|