/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/type_conversions.h" #include "hphp/runtime/base/zend_functions.h" #include "hphp/system/systemlib.h" namespace HPHP { ////////////////////////////////////////////////////////////////////// bool cellIsPlausible(const Cell* cell) { assert(cell); assert(cell->m_type != KindOfRef); auto assertPtr = [](void* ptr) { assert(ptr && (uintptr_t(ptr) % sizeof(ptr) == 0)); }; switch (cell->m_type) { case KindOfUninit: case KindOfNull: break; case KindOfBoolean: assert(cell->m_data.num == 0 || cell->m_data.num == 1); break; case KindOfInt64: case KindOfDouble: break; case KindOfStaticString: assertPtr(cell->m_data.pstr); assert(cell->m_data.pstr->isStatic()); break; case KindOfString: assertPtr(cell->m_data.pstr); assert(is_refcount_realistic(cell->m_data.pstr->getCount())); break; case KindOfArray: assertPtr(cell->m_data.parr); assert(is_refcount_realistic(cell->m_data.parr->getCount())); break; case KindOfObject: assertPtr(cell->m_data.pobj); assert(!cell->m_data.pobj->isStatic()); break; case KindOfRef: assert(!"KindOfRef found in a Cell"); break; default: not_reached(); } return true; } bool tvIsPlausible(const TypedValue* tv) { assert(tv); if (tv->m_type == KindOfRef) { assert(tv->m_data.pref); assert(uintptr_t(tv->m_data.pref) % sizeof(void*) == 0); assert(is_refcount_realistic(tv->m_data.pref->getCount())); tv = tv->m_data.pref->tv(); } return cellIsPlausible(tv); } bool varIsPlausible(const Var* var) { assert(var); assert(var->m_type == KindOfRef); return tvIsPlausible(var); } inline void tvUnboxIfNeeded(TypedValue *tv) { if (tv->m_type == KindOfRef) { tvUnbox(tv); } } void tvCastToBooleanInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); bool b; switch (tv->m_type) { case KindOfUninit: case KindOfNull: b = false; break; case KindOfBoolean: return; case KindOfInt64: b = (tv->m_data.num != 0LL); break; case KindOfDouble: b = (tv->m_data.dbl != 0); break; case KindOfStaticString: b = tv->m_data.pstr->toBoolean(); break; case KindOfString: b = tv->m_data.pstr->toBoolean(); tvDecRefStr(tv); break; case KindOfArray: b = (!tv->m_data.parr->empty()); tvDecRefArr(tv); break; case KindOfObject: b = (tv->m_data.pobj != nullptr); tvDecRefObj(tv); break; default: assert(false); b = false; break; } tv->m_data.num = b; tv->m_type = KindOfBoolean; } void tvCastToDoubleInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); double d; switch (tv->m_type) { case KindOfUninit: case KindOfNull: d = 0.0; break; case KindOfBoolean: assert(tv->m_data.num == 0LL || tv->m_data.num == 1LL); case KindOfInt64: d = (double)(tv->m_data.num); break; case KindOfDouble: return; case KindOfStaticString: d = tv->m_data.pstr->toDouble(); break; case KindOfString: d = tv->m_data.pstr->toDouble(); tvDecRefStr(tv); break; case KindOfArray: { d = (double)(tv->m_data.parr->empty() ? 0LL : 1LL); tvDecRefArr(tv); break; } case KindOfObject: { d = tv->m_data.pobj->o_toDouble(); tvDecRefObj(tv); break; } default: assert(false); d = 0.0; break; } tv->m_data.dbl = d; tv->m_type = KindOfDouble; } void cellCastToInt64InPlace(Cell* cell) { assert(cellIsPlausible(cell)); int64_t i; switch (cell->m_type) { case KindOfUninit: case KindOfNull: cell->m_data.num = 0LL; // Fall through case KindOfBoolean: assert(cell->m_data.num == 0LL || cell->m_data.num == 1LL); cell->m_type = KindOfInt64; // Fall through case KindOfInt64: return; case KindOfDouble: { i = toInt64(cell->m_data.dbl); break; } case KindOfStaticString: i = (cell->m_data.pstr->toInt64()); break; case KindOfString: { i = cell->m_data.pstr->toInt64(); tvDecRefStr(cell); break; } case KindOfArray: i = cell->m_data.parr->empty() ? 0 : 1; tvDecRefArr(cell); break; case KindOfObject: i = cell->m_data.pobj->o_toInt64(); tvDecRefObj(cell); break; default: not_reached(); } cell->m_data.num = i; cell->m_type = KindOfInt64; } void tvCastToInt64InPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); cellCastToInt64InPlace(tv); } double tvCastToDouble(TypedValue* tv) { assert(tvIsPlausible(tv)); if (tv->m_type == KindOfRef) { tv = tv->m_data.pref->tv(); } switch(tv->m_type) { case KindOfUninit: case KindOfNull: return 0; case KindOfBoolean: assert(tv->m_data.num == 0LL || tv->m_data.num == 1LL); // Fall through case KindOfInt64: return (double)(tv->m_data.num); case KindOfDouble: return tv->m_data.dbl; case KindOfStaticString: case KindOfString: return tv->m_data.pstr->toDouble(); case KindOfArray: return tv->m_data.parr->empty() ? 0.0 : 1.0; case KindOfObject: return tv->m_data.pobj->o_toDouble(); default: not_reached(); } } const StaticString s_1("1"), s_Array("Array"); void tvCastToStringInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); StringData * s; switch (tv->m_type) { case KindOfUninit: case KindOfNull: s = empty_string.get(); goto static_string; case KindOfBoolean: s = tv->m_data.num ? s_1.get() : empty_string.get(); goto static_string; case KindOfInt64: s = buildStringData(tv->m_data.num); break; case KindOfDouble: s = buildStringData(tv->m_data.dbl); break; case KindOfStaticString: case KindOfString: return; case KindOfArray: s = s_Array.get(); tvDecRefArr(tv); goto static_string; case KindOfObject: // For objects, we fall back on the Variant machinery tvAsVariant(tv) = tv->m_data.pobj->t___tostring(); return; default: not_reached(); } s->incRefCount(); tv->m_data.pstr = s; tv->m_type = KindOfString; return; static_string: tv->m_data.pstr = s; tv->m_type = KindOfStaticString; } StringData* tvCastToString(TypedValue* tv) { assert(tvIsPlausible(tv)); if (tv->m_type == KindOfRef) { tv = tv->m_data.pref->tv(); } StringData* s; switch (tv->m_type) { case KindOfUninit: case KindOfNull: return empty_string.get(); case KindOfBoolean: return tv->m_data.num ? s_1.get() : empty_string.get(); case KindOfInt64: s = buildStringData(tv->m_data.num); break; case KindOfDouble: s = buildStringData(tv->m_data.dbl); break; case KindOfStaticString: return tv->m_data.pstr; case KindOfString: s = tv->m_data.pstr; break; case KindOfArray: return s_Array.get(); case KindOfObject: return tv->m_data.pobj->t___tostring().detach(); default: not_reached(); } s->incRefCount(); return s; } void tvCastToArrayInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); ArrayData * a; switch (tv->m_type) { case KindOfUninit: case KindOfNull: a = ArrayData::Create(); break; case KindOfBoolean: case KindOfInt64: case KindOfDouble: case KindOfStaticString: a = ArrayData::Create(tvAsVariant(tv)); break; case KindOfString: { a = ArrayData::Create(tvAsVariant(tv)); tvDecRefStr(tv); break; } case KindOfArray: return; case KindOfObject: { // For objects, we fall back on the Variant machinery tvAsVariant(tv) = tv->m_data.pobj->o_toArray(); return; } default: assert(false); a = ArrayData::Create(); break; } tv->m_data.parr = a; tv->m_type = KindOfArray; tv->m_data.parr->incRefCount(); } void tvCastToObjectInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); ObjectData* o; switch (tv->m_type) { case KindOfUninit: case KindOfNull: o = SystemLib::AllocStdClassObject(); break; case KindOfBoolean: case KindOfInt64: case KindOfDouble: case KindOfStaticString: { o = SystemLib::AllocStdClassObject(); o->o_set("scalar", tvAsVariant(tv)); break; } case KindOfString: { o = SystemLib::AllocStdClassObject(); o->o_set("scalar", tvAsVariant(tv)); tvDecRefStr(tv); break; } case KindOfArray: { // For arrays, we fall back on the Variant machinery tvAsVariant(tv) = tv->m_data.parr->toObject(); return; } case KindOfObject: return; default: assert(false); o = SystemLib::AllocStdClassObject(); break; } tv->m_data.pobj = o; tv->m_type = KindOfObject; tv->m_data.pobj->incRefCount(); } bool tvCoerceParamToBooleanInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); if (tv->m_type == KindOfArray || tv->m_type == KindOfObject) { return false; } tvCastToBooleanInPlace(tv); return true; } bool tvCanBeCoercedToNumber(TypedValue* tv) { switch (tv->m_type) { case KindOfStaticString: case KindOfString: StringData* s; DataType type; s = tv->m_data.pstr; type = is_numeric_string(s->data(), s->size(), nullptr, nullptr); if (type != KindOfDouble && type != KindOfInt64) { return false; } break; case KindOfArray: case KindOfObject: return false; default: break; } return true; } bool tvCoerceParamToInt64InPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); if (!tvCanBeCoercedToNumber(tv)) { return false; } tvCastToInt64InPlace(tv); return true; } bool tvCoerceParamToDoubleInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); if (!tvCanBeCoercedToNumber(tv)) { return false; } tvCastToDoubleInPlace(tv); return true; } bool tvCoerceParamToStringInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); switch (tv->m_type) { case KindOfArray: return false; case KindOfObject: try { tvAsVariant(tv) = tv->m_data.pobj->t___tostring(); return true; } catch (BadTypeConversionException &e) { } return false; default: break; } tvCastToStringInPlace(tv); return true; } bool tvCoerceParamToArrayInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); if (tv->m_type == KindOfArray) { return true; } else if (tv->m_type == KindOfObject) { tvAsVariant(tv) = tv->m_data.pobj->o_toArray(); return true; } return false; } bool tvCoerceParamToObjectInPlace(TypedValue* tv) { assert(tvIsPlausible(tv)); tvUnboxIfNeeded(tv); return tv->m_type == KindOfObject; } /////////////////////////////////////////////////////////////////////////////// }