Arquivos
hhvm/hphp/runtime/base/shared/shared_variant.cpp
T
smith 22058efe41 Allow different RefData and TypedValue layouts
Fixing various bugs all over the VM that make assumptions about RefData
and TypedValue layout.  Here are the assumptions fixed by this diff:

offsetof(RefData, m_tv) == 0.  Both JIT's assumed this in many subtle
ways, by punning RefData* as TypedValue* without adding an offset.
This assumption also causes RefData._count to overlap TypedValue.m_aux,
which constraints TypedValue layout.

offsetof(TypedValue, m_data) == 0.  gen_ext_hhvm.php assumes you
can cast TypedValue* to Value*; the JITs often weren't using
offsetof(TypedValue, m_data) in their addressing calculations.  HHIR
assumed return-by-value TV's have m_data/m_type in rax/rdx, which
can change when TV layout changes.

offsetof(TypedValue, m_type) > 8 is an assumption baked into the
pass-by-value register assignment logic in HHIR's codegen.cpp; if
the type is in the low word, register assignment is swapped.

sizeof(TypedValue::m_type) == 4.  We used dword-sized operations
in both JIT's when accessing m_type.  Now, we use helper functions
that are sensitive to sizeof(DataType)

Configuration:
DEBUG=: (opt)  same layouts as trunk for RefData & TypedValue
DEBUG=1: (dbg) new RefData layout (m_tv doesn't overlap RefData::_count)
PACKED_TV=1, DEBUG=*:  new RefData and TypedValue layout.
2013-04-01 11:51:31 -07:00

440 linhas
12 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. |
+----------------------------------------------------------------------+
*/
#include <runtime/base/shared/shared_variant.h>
#include <runtime/ext/ext_variable.h>
#include <runtime/ext/ext_apc.h>
#include <runtime/base/shared/shared_map.h>
#include <runtime/base/runtime_option.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
SharedVariant::SharedVariant(CVarRef source, bool serialized,
bool inner /* = false */,
bool unserializeObj /* = false */)
: m_shouldCache(false), m_flags(0) {
assert(!serialized || source.isString());
m_count = 1;
m_type = source.getType();
switch (m_type) {
case KindOfBoolean:
{
m_data.num = source.toBoolean();
break;
}
case KindOfInt64:
{
m_type = KindOfInt64;
m_data.num = source.toInt64();
break;
}
case KindOfDouble:
{
m_data.dbl = source.toDouble();
break;
}
case KindOfStaticString:
case KindOfString:
{
String s = source.toString();
if (serialized) {
m_type = KindOfObject;
// It is priming, and there might not be the right class definitions
// for unserialization.
s = apc_reserialize(s);
}
m_data.str = s->copy(true);
break;
}
case KindOfArray:
{
ArrayData *arr = source.getArrayData();
if (!inner) {
// only need to call hasInternalReference() on the toplevel array
PointerSet seen;
if (arr->hasInternalReference(seen)) {
setSerializedArray();
m_shouldCache = true;
String s = apc_serialize(source);
m_data.str = new StringData(s.data(), s.size(), CopyMalloc);
break;
}
}
if (arr->isVectorData()) {
setIsVector();
m_data.vec = new (arr->size()) VectorData();
for (ArrayIter it(arr); !it.end(); it.next()) {
SharedVariant* val = Create(it.secondRef(), false, true,
unserializeObj);
if (val->m_shouldCache) m_shouldCache = true;
m_data.vec->vals()[m_data.vec->m_size++] = val;
}
} else {
m_data.map = ImmutableMap::Create(arr, unserializeObj, m_shouldCache);
}
break;
}
case KindOfUninit:
case KindOfNull:
{
break;
}
default:
{
assert(source.isObject());
m_shouldCache = true;
if (unserializeObj) {
// This assumes hasInternalReference(seen, true) is false
ImmutableObj* obj = new ImmutableObj(source.getObjectData());
m_data.obj = obj;
setIsObj();
} else {
String s = apc_serialize(source);
m_data.str = new StringData(s.data(), s.size(), CopyMalloc);
}
break;
}
}
}
HOT_FUNC
Variant SharedVariant::toLocal() {
switch (m_type) {
case KindOfBoolean:
{
return (bool)m_data.num;
}
case KindOfInt64:
{
return m_data.num;
}
case KindOfDouble:
{
return m_data.dbl;
}
case KindOfStaticString:
{
return m_data.str;
}
case KindOfString:
{
return NEW(StringData)(this);
}
case KindOfArray:
{
if (getSerializedArray()) {
return apc_unserialize(String(m_data.str->data(), m_data.str->size(),
AttachLiteral));
}
return NEW(SharedMap)(this);
}
case KindOfUninit:
case KindOfNull:
{
return null_variant;
}
default:
{
assert(m_type == KindOfObject);
if (getIsObj()) {
return m_data.obj->getObject();
}
return apc_unserialize(String(m_data.str->data(), m_data.str->size(),
AttachLiteral));
}
}
}
void SharedVariant::dump(std::string &out) {
out += "ref(";
out += boost::lexical_cast<string>(m_count);
out += ") ";
switch (m_type) {
case KindOfBoolean:
out += "boolean: ";
out += m_data.num ? "true" : "false";
break;
case KindOfInt64:
out += "int: ";
out += boost::lexical_cast<string>(m_data.num);
break;
case KindOfDouble:
out += "double: ";
out += boost::lexical_cast<string>(m_data.dbl);
break;
case KindOfStaticString:
case KindOfString:
out += "string(";
out += boost::lexical_cast<string>(stringLength());
out += "): ";
out += stringData();
break;
case KindOfArray:
if (getSerializedArray()) {
out += "array: ";
out += m_data.str->data();
} else {
SharedMap(this).dump(out);
}
break;
case KindOfUninit:
case KindOfNull:
out += "null";
break;
default:
out += "object: ";
out += m_data.str->data();
break;
}
out += "\n";
}
SharedVariant::~SharedVariant() {
switch (m_type) {
case KindOfObject:
if (getIsObj()) {
delete m_data.obj;
break;
}
// otherwise fall through
case KindOfString:
m_data.str->destruct();
break;
case KindOfArray:
{
if (getSerializedArray()) {
m_data.str->destruct();
break;
}
if (getIsVector()) {
delete m_data.vec;
} else {
ImmutableMap::Destroy(m_data.map);
}
}
break;
default:
break;
}
}
///////////////////////////////////////////////////////////////////////////////
HOT_FUNC
int SharedVariant::getIndex(const StringData* key) {
assert(is(KindOfArray));
if (getIsVector()) return -1;
return m_data.map->indexOf(key);
}
int SharedVariant::getIndex(int64_t key) {
assert(is(KindOfArray));
if (getIsVector()) {
if (key < 0 || (size_t) key >= m_data.vec->m_size) return -1;
return key;
}
return m_data.map->indexOf(key);
}
Variant SharedVariant::getKey(ssize_t pos) const {
assert(is(KindOfArray));
if (getIsVector()) {
assert(pos < (ssize_t) m_data.vec->m_size);
return pos;
}
return m_data.map->getKeyIndex(pos)->toLocal();
}
HOT_FUNC
SharedVariant* SharedVariant::getValue(ssize_t pos) const {
assert(is(KindOfArray));
if (getIsVector()) {
assert(pos < (ssize_t) m_data.vec->m_size);
return m_data.vec->vals()[pos];
}
return m_data.map->getValIndex(pos);
}
void SharedVariant::loadElems(ArrayData *&elems,
const SharedMap &sharedMap,
bool mapInit /* = false */) {
assert(is(KindOfArray));
uint count = arrSize();
bool isVector = getIsVector();
ArrayInit ai = mapInit ? ArrayInit(count, ArrayInit::mapInit) :
isVector ? ArrayInit(count, ArrayInit::vectorInit) :
ArrayInit(count);
if (isVector) {
for (uint i = 0; i < count; i++) {
ai.set(sharedMap.getValueRef(i));
}
} else {
for (uint i = 0; i < count; i++) {
ai.add(m_data.map->getKeyIndex(i)->toLocal(), sharedMap.getValueRef(i),
true);
}
}
elems = ai.create();
if (elems->isStatic()) elems = elems->copy();
}
int SharedVariant::countReachable() const {
int count = 1;
if (getType() == KindOfArray) {
int size = arrSize();
if (!getIsVector()) {
count += size; // for keys
}
for (int i = 0; i < size; i++) {
SharedVariant* p = getValue(i);
count += p->countReachable(); // for values
}
}
return count;
}
SharedVariant *SharedVariant::Create
(CVarRef source, bool serialized, bool inner /* = false */,
bool unserializeObj /* = false*/) {
SharedVariant *wrapped = source.getSharedVariant();
if (wrapped && !unserializeObj) {
wrapped->incRef();
// static cast should be enough
return (SharedVariant *)wrapped;
}
return new SharedVariant(source, serialized, inner, unserializeObj);
}
SharedVariant* SharedVariant::convertObj(CVarRef var) {
if (!var.is(KindOfObject) || getObjAttempted()) {
return nullptr;
}
setObjAttempted();
ObjectData *obj = var.getObjectData();
if (obj->instanceof(SystemLib::s_SerializableClass)) {
// should also check the object itself
return nullptr;
}
PointerSet seen;
if (obj->hasInternalReference(seen, true)) {
return nullptr;
}
SharedVariant *tmp = new SharedVariant(var, false, true, true);
tmp->setObjAttempted();
return tmp;
}
int32_t SharedVariant::getSpaceUsage() const {
int32_t size = sizeof(SharedVariant);
if (!IS_REFCOUNTED_TYPE(m_type)) return size;
switch (m_type) {
case KindOfObject:
if (getIsObj()) {
return size + m_data.obj->getSpaceUsage();
}
// fall through
case KindOfString:
size += sizeof(StringData) + m_data.str->size();
break;
default:
assert(is(KindOfArray));
if (getSerializedArray()) {
size += sizeof(StringData) + m_data.str->size();
} else if (getIsVector()) {
size += sizeof(VectorData) +
sizeof(SharedVariant*) * m_data.vec->m_size;
for (size_t i = 0; i < m_data.vec->m_size; i++) {
size += m_data.vec->vals()[i]->getSpaceUsage();
}
} else {
ImmutableMap *map = m_data.map;
size += map->getStructSize();
for (int i = 0; i < map->size(); i++) {
size += map->getKeyIndex(i)->getSpaceUsage();
size += map->getValIndex(i)->getSpaceUsage();
}
}
break;
}
return size;
}
void SharedVariant::getStats(SharedVariantStats *stats) const {
stats->initStats();
stats->variantCount = 1;
switch (m_type) {
case KindOfUninit:
case KindOfNull:
case KindOfBoolean:
case KindOfInt64:
case KindOfDouble:
case KindOfStaticString:
stats->dataSize = sizeof(m_data.dbl);
stats->dataTotalSize = sizeof(SharedVariant);
break;
case KindOfObject:
if (getIsObj()) {
SharedVariantStats childStats;
m_data.obj->getSizeStats(&childStats);
stats->addChildStats(&childStats);
break;
}
// fall through
case KindOfString:
stats->dataSize = m_data.str->size();
stats->dataTotalSize = sizeof(SharedVariant) + sizeof(StringData) +
stats->dataSize;
break;
default:
assert(is(KindOfArray));
if (getSerializedArray()) {
stats->dataSize = m_data.str->size();
stats->dataTotalSize = sizeof(SharedVariant) + sizeof(StringData) +
stats->dataSize;
break;
}
if (getIsVector()) {
stats->dataTotalSize = sizeof(SharedVariant) + sizeof(VectorData);
stats->dataTotalSize += sizeof(SharedVariant*) * m_data.vec->m_size;
for (size_t i = 0; i < m_data.vec->m_size; i++) {
SharedVariant *v = m_data.vec->vals()[i];
SharedVariantStats childStats;
v->getStats(&childStats);
stats->addChildStats(&childStats);
}
} else {
ImmutableMap *map = m_data.map;
stats->dataTotalSize = sizeof(SharedVariant) + map->getStructSize();
for (int i = 0; i < map->size(); i++) {
SharedVariantStats childStats;
map->getKeyIndex(i)->getStats(&childStats);
stats->addChildStats(&childStats);
map->getValIndex(i)->getStats(&childStats);
stats->addChildStats(&childStats);
}
}
break;
}
}
///////////////////////////////////////////////////////////////////////////////
}