Arquivos
hhvm/hphp/runtime/base/class_info.cpp
T
Paul Tarjan fef62f03a2 kill VM namespace
Now that HHVM is the default runtime, this namespace doesn't mean anything.
2013-05-15 13:05:05 -07:00

825 linhas
24 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/array/array_util.h>
#include <runtime/base/class_info.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/externals.h>
#include <runtime/base/hphp_system.h>
#include <runtime/base/variable_serializer.h>
#include <runtime/base/variable_unserializer.h>
#include <util/util.h>
#include <util/lock.h>
#include <util/logger.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// statics
bool ClassInfo::s_loaded = false;
ClassInfo *ClassInfo::s_systemFuncs = nullptr;
ClassInfo::ClassMap ClassInfo::s_class_like;
ClassInfoHook *ClassInfo::s_hook = nullptr;
Array ClassInfo::GetSystemFunctions() {
assert(s_loaded);
Array ret = Array::Create();
if (s_systemFuncs) {
const MethodVec &methods = s_systemFuncs->getMethodsVec();
for (unsigned i = 0; i < methods.size(); i++) {
ret.append(methods[i]->name);
}
}
return ret;
}
Array ClassInfo::GetUserFunctions() {
assert(s_loaded);
Array ret = Array::Create();
if (s_hook) {
Array dyn = s_hook->getUserFunctions();
if (!dyn.isNull()) {
ret.merge(dyn);
// De-dup values, then renumber (for aesthetics).
ret = ArrayUtil::StringUnique(ret).toArrRef();
ret->renumber();
}
}
return ret;
}
const ClassInfo::MethodInfo *ClassInfo::FindSystemFunction(CStrRef name) {
assert(!name.isNull());
assert(s_loaded);
return s_systemFuncs->getMethodInfo(name);
}
const ClassInfo::MethodInfo *ClassInfo::FindFunction(CStrRef name) {
assert(!name.isNull());
assert(s_loaded);
const MethodInfo *ret = s_systemFuncs->getMethodInfo(name);
if (ret == nullptr && s_hook) {
ret = s_hook->findFunction(name);
}
return ret;
}
const ClassInfo *ClassInfo::FindClassInterfaceOrTrait(CStrRef name) {
assert(!name.isNull());
assert(s_loaded);
if (s_hook) {
const ClassInfo *r = s_hook->findClassLike(name);
if (r) return r;
}
ClassMap::const_iterator iter = s_class_like.find(name);
if (iter != s_class_like.end()) {
return iter->second->getDeclared();
}
return 0;
}
const ClassInfo *ClassInfo::FindClass(CStrRef name) {
if (const ClassInfo *r = FindClassInterfaceOrTrait(name)) {
return r->getAttribute() & (IsTrait|IsInterface) ? 0 : r;
}
return 0;
}
const ClassInfo *ClassInfo::FindInterface(CStrRef name) {
if (const ClassInfo *r = FindClassInterfaceOrTrait(name)) {
return r->getAttribute() & IsInterface ? r : 0;
}
return 0;
}
const ClassInfo *ClassInfo::FindTrait(CStrRef name) {
if (const ClassInfo *r = FindClassInterfaceOrTrait(name)) {
return r->getAttribute() & IsTrait ? r : 0;
}
return 0;
}
const ClassInfo *ClassInfo::FindSystemClassInterfaceOrTrait(CStrRef name) {
assert(!name.isNull());
assert(s_loaded);
ClassMap::const_iterator iter = s_class_like.find(name);
if (iter != s_class_like.end()) {
const ClassInfo *ci = iter->second;
if (ci->m_attribute & IsSystem) return ci;
}
return 0;
}
const ClassInfo *ClassInfo::FindSystemClass(CStrRef name) {
if (const ClassInfo *r = FindSystemClassInterfaceOrTrait(name)) {
return r->getAttribute() & (IsTrait|IsInterface) ? 0 : r;
}
return 0;
}
const ClassInfo *ClassInfo::FindSystemInterface(CStrRef name) {
if (const ClassInfo *r = FindSystemClassInterfaceOrTrait(name)) {
return r->getAttribute() & IsInterface ? r : 0;
}
return 0;
}
const ClassInfo *ClassInfo::FindSystemTrait(CStrRef name) {
if (const ClassInfo *r = FindSystemClassInterfaceOrTrait(name)) {
return r->getAttribute() & IsTrait ? r : 0;
}
return 0;
}
Array ClassInfo::GetClassLike(unsigned mask, unsigned value) {
assert(s_loaded);
Array ret = Array::Create();
for (ClassMap::const_iterator iter = s_class_like.begin();
iter != s_class_like.end(); ++iter) {
const ClassInfo *info = iter->second->getDeclared();
if (!info || (info->m_attribute & mask) != value) continue;
ret.append(info->m_name);
}
if (s_hook) {
if (value & IsInterface) {
Array dyn = s_hook->getInterfaces();
if (!dyn.isNull()) {
ret.merge(dyn);
// De-dup values, then renumber (for aesthetics).
ret = ArrayUtil::StringUnique(ret).toArrRef();
ret->renumber();
}
} else if (value & IsTrait) {
Array dyn = s_hook->getTraits();
if (!dyn.isNull()) {
ret.merge(dyn);
// De-dup values, then renumber (for aesthetics).
ret = ArrayUtil::StringUnique(ret).toArrRef();
ret->renumber();
}
} else {
Array dyn = s_hook->getClasses();
if (!dyn.isNull()) {
ret.merge(dyn);
// De-dup values, then renumber (for aesthetics).
ret = ArrayUtil::StringUnique(ret).toArrRef();
ret->renumber();
}
}
}
return ret;
}
ClassInfo::ConstantInfo::ConstantInfo() :
valueLen(0), callback(nullptr), deferred(true) {
}
CVarRef ClassInfo::ConstantInfo::getDeferredValue() const {
assert(deferred);
if (callback) {
CVarRef (*f)()=(CVarRef(*)())callback;
return (*f)();
}
SystemGlobals* g = get_global_variables();
return g->stgv_Variant[valueLen];
}
Variant ClassInfo::ConstantInfo::getValue() const {
if (deferred) {
return getDeferredValue();
}
if (!svalue.empty()) {
try {
VariableUnserializer vu(svalue.data(), svalue.size(),
VariableUnserializer::Serialize);
return vu.unserialize();
} catch (Exception &e) {
assert(false);
}
}
return value;
}
void ClassInfo::ConstantInfo::setValue(CVarRef value) {
VariableSerializer vs(VariableSerializer::Serialize);
String s = vs.serialize(value, true);
svalue = string(s.data(), s.size());
deferred = false;
}
void ClassInfo::ConstantInfo::setStaticValue(CVarRef v) {
value = v;
value.setEvalScalar();
deferred = false;
}
void ClassInfo::InitializeSystemConstants() {
assert(s_loaded);
const ConstantMap &scm = s_systemFuncs->getConstants();
for (ConstantMap::const_iterator it = scm.begin(); it != scm.end(); ++it) {
ConstantInfo* ci = it->second;
if (ci->isDynamic()) {
Unit::defDynamicSystemConstant(ci->name.get(), ci);
} else {
Variant v = ci->getValue();
bool DEBUG_ONLY res = Unit::defCns(ci->name.get(),
v.asTypedValue(), true);
assert(res);
}
}
}
Array ClassInfo::GetSystemConstants() {
assert(s_loaded);
Array res;
const ConstantMap &scm = s_systemFuncs->getConstants();
for (ConstantMap::const_iterator it = scm.begin(); it != scm.end(); ++it) {
if (!it->second->isDynamic()) {
res.set(it->second->name, it->second->getValue());
}
}
return res;
}
Array ClassInfo::GetConstants() {
assert(s_loaded);
Array res;
Array dyn;
{
const ConstantMap &scm = s_systemFuncs->getConstants();
for (ConstantMap::const_iterator it = scm.begin(); it != scm.end(); ++it) {
res.set(it->second->name, it->second->getValue());
}
}
if (s_hook) {
dyn = s_hook->getConstants();
if (!dyn.isNull()) {
res.merge(dyn);
}
}
return res;
}
ClassInfo::UserAttributeInfo::UserAttributeInfo() {
}
Variant ClassInfo::UserAttributeInfo::getValue() const {
return value;
}
void ClassInfo::UserAttributeInfo::setStaticValue(CVarRef v) {
value = v;
value.setEvalScalar();
}
bool ClassInfo::GetClassMethods(MethodVec &ret, CStrRef classname,
int type /* = 0 */) {
if (classname.empty()) return false;
const ClassInfo *classInfo = nullptr;
switch (type) {
case 0:
classInfo = FindClassInterfaceOrTrait(classname);
break;
case 1:
classInfo = FindClass(classname);
break;
case 2:
classInfo = FindInterface(classname);
break;
case 3:
classInfo = FindTrait(classname);
break;
default:
assert(false);
}
if (!classInfo) return false;
return GetClassMethods(ret, classInfo);
}
bool ClassInfo::GetClassMethods(MethodVec &ret, const ClassInfo *classInfo) {
const ClassInfo::MethodVec &methods = classInfo->getMethodsVec();
ret.insert(ret.end(), methods.begin(), methods.end());
if (!(classInfo->getAttribute() & (IsInterface|IsTrait))) {
CStrRef parentClass = classInfo->getParentClass();
if (!parentClass.empty()) {
if (!GetClassMethods(ret, parentClass, 1)) return false;
}
}
const ClassInfo::InterfaceVec &interfaces =
classInfo->getInterfacesVec();
for (unsigned int i = 0; i < interfaces.size(); i++) {
if (!GetClassMethods(ret, interfaces[i], 2)) return false;
}
return true;
}
void ClassInfo::GetClassSymbolNames(CArrRef names, bool interface, bool trait,
std::vector<String> &classes,
std::vector<String> *clsMethods,
std::vector<String> *clsProperties,
std::vector<String> *clsConstants) {
if (clsMethods || clsProperties || clsConstants) {
for (ArrayIter iter(names); iter; ++iter) {
String clsname = iter.second().toString();
classes.push_back(clsname);
const ClassInfo *cls;
if (interface) {
cls = FindInterface(clsname.data());
} else if (trait) {
cls = FindTrait(clsname.data());
} else {
try {
cls = FindClass(clsname.data());
} catch (Exception &e) {
Logger::Error("Caught exception %s", e.getMessage().c_str());
continue;
} catch(...) {
Logger::Error("Caught unknown exception");
continue;
}
}
assert(cls);
if (clsMethods) {
const ClassInfo::MethodVec &methods = cls->getMethodsVec();
for (unsigned int i = 0; i < methods.size(); i++) {
clsMethods->push_back(clsname + "::" + methods[i]->name);
}
}
if (clsProperties) {
const ClassInfo::PropertyVec &properties = cls->getPropertiesVec();
for (ClassInfo::PropertyVec::const_iterator iter = properties.begin();
iter != properties.end(); ++iter) {
clsProperties->push_back(clsname + "::$" + (*iter)->name);
}
}
if (clsConstants) {
const ClassInfo::ConstantVec &constants = cls->getConstantsVec();
for (ClassInfo::ConstantVec::const_iterator iter = constants.begin();
iter != constants.end(); ++iter) {
clsConstants->push_back(clsname + "::" + (*iter)->name);
}
}
}
} else {
for (ArrayIter iter(names); iter; ++iter) {
classes.push_back(iter.second().toString());
}
}
}
void ClassInfo::GetSymbolNames(std::vector<String> &classes,
std::vector<String> &functions,
std::vector<String> &constants,
std::vector<String> *clsMethods,
std::vector<String> *clsProperties,
std::vector<String> *clsConstants) {
static unsigned int methodSize = 128;
static unsigned int propSize = 128;
static unsigned int constSize = 128;
if (clsMethods) {
clsMethods->reserve(methodSize);
}
if (clsProperties) {
clsProperties->reserve(propSize);
}
if (clsConstants) {
clsConstants->reserve(constSize);
}
GetClassSymbolNames(GetClasses(), false, false, classes,
clsMethods, clsProperties, clsConstants);
GetClassSymbolNames(GetInterfaces(), true, false, classes,
clsMethods, clsProperties, clsConstants);
if (clsMethods && methodSize < clsMethods->size()) {
methodSize = clsMethods->size();
}
if (clsProperties && propSize < clsProperties->size()) {
propSize = clsProperties->size();
}
if (constSize && constSize < clsConstants->size()) {
constSize = clsConstants->size();
}
Array funcs1 = ClassInfo::GetSystemFunctions();
Array funcs2 = ClassInfo::GetUserFunctions();
functions.reserve(funcs1.size() + funcs2.size());
for (ArrayIter iter(funcs1); iter; ++iter) {
functions.push_back(iter.second().toString());
}
for (ArrayIter iter(funcs2); iter; ++iter) {
functions.push_back(iter.second().toString());
}
Array consts = GetConstants();
constants.reserve(consts.size());
for (ArrayIter iter(consts); iter; ++iter) {
constants.push_back(iter.first().toString());
}
}
///////////////////////////////////////////////////////////////////////////////
// ClassInfo
const ClassInfo *ClassInfo::getDeclared() const {
if (m_attribute & IsRedeclared) {
return getCurrentOrNull();
} else if (m_attribute & IsVolatile) {
return *(bool*)((char*)get_global_variables() + m_cdec_offset) ? this : 0;
} else {
return this;
}
}
const ClassInfo *ClassInfo::getParentClassInfo() const {
CStrRef parentName = getParentClass();
if (parentName.empty()) return nullptr;
return FindClass(parentName);
}
ClassInfo::MethodInfo *ClassInfo::getMethodInfo(CStrRef name) const {
assert(!name.isNull());
const MethodMap &methods = getMethods();
MethodMap::const_iterator iter = methods.find(name);
if (iter != methods.end()) {
ClassInfo::MethodInfo *m = iter->second;
if (m->attribute & (IsVolatile|IsRedeclared)) {
return m->getDeclared();
}
return m;
}
return nullptr;
}
ClassInfo::MethodInfo *ClassInfo::hasMethod(CStrRef name,
ClassInfo* &classInfo,
bool interfaces /* = false */)
const {
assert(!name.isNull());
classInfo = (ClassInfo *)this;
const MethodMap &methods = getMethods();
MethodMap::const_iterator it = methods.find(name);
if (it != methods.end()) {
assert(!(it->second->attribute & (IsVolatile|IsRedeclared)));
return it->second;
}
ClassInfo::MethodInfo *result = nullptr;
const ClassInfo *parent = getParentClassInfo();
if (parent) result = parent->hasMethod(name, classInfo);
if (result || !interfaces || !(m_attribute & IsAbstract)) return result;
// TODO: consider caching the iface lookups
const InterfaceVec &ifaces = getInterfacesVec();
for (InterfaceVec::const_iterator it = ifaces.begin();
it != ifaces.end(); ++it) {
const ClassInfo *iface = FindInterface(*it);
if (iface) result = iface->hasMethod(name, classInfo, true);
if (result) return result;
}
return nullptr;
}
///////////////////////////////////////////////////////////////////////////////
// load functions
static String makeStaticString(const char *s) {
if (!s) {
return null_string;
}
return StringData::GetStaticString(s);
}
void ClassInfo::ReadUserAttributes(const char **&p,
std::vector<const UserAttributeInfo*> &userAttrVec) {
while (*p) {
UserAttributeInfo *userAttr = new UserAttributeInfo();
userAttr->name = makeStaticString(*p++);
const char *len = *p++;
const char *valueText = *p++;
int64_t valueLen = (int64_t)len;
VariableUnserializer vu(valueText,
valueLen,
VariableUnserializer::Serialize);
userAttr->setStaticValue(vu.unserialize());
userAttrVec.push_back(userAttr);
}
}
ClassInfo::MethodInfo *ClassInfo::MethodInfo::getDeclared() {
assert(!(attribute & ClassInfo::IsRedeclared));
assert(!(attribute & ClassInfo::IsVolatile));
return this;
}
ClassInfo::MethodInfo::MethodInfo(const char **&p) {
attribute = (Attribute)(int64_t)(*p++);
name = makeStaticString(*p++);
docComment = 0;
if (attribute & ClassInfo::IsRedeclared) {
volatile_redec_offset = (int)(int64_t)(*p++);
while (*p) {
MethodInfo *m = new MethodInfo(p);
parameters.push_back((ParameterInfo*)(void*)m);
}
} else {
file = *p++;
line1 = (int)(int64_t)(*p++);
line2 = (int)(int64_t)(*p++);
if (attribute & IsVolatile) {
volatile_redec_offset = (int)(int64_t)(*p++);
}
if (attribute & HasDocComment) {
docComment = *p++;
}
if (attribute & IsSystem) {
returnType = (DataType)(int64_t)(*p++);
}
while (*p) {
ParameterInfo *parameter = new ParameterInfo();
parameter->attribute = (Attribute)(int64_t)(*p++);
parameter->name = *p++;
parameter->type = *p++;
if (attribute & IsSystem) {
parameter->argType = (DataType)(int64_t)(*p++);
}
parameter->value = *p++;
parameter->valueLen = (int64_t)*p++;
parameter->valueText = *p++;
parameter->valueTextLen = (int64_t)*p++;
ClassInfo::ReadUserAttributes(p, parameter->userAttrs);
p++;
parameters.push_back(parameter);
}
p++;
while (*p) {
ConstantInfo *staticVariable = new ConstantInfo();
staticVariable->name = makeStaticString(*p++);
staticVariable->valueLen = (int64_t)(*p++);
staticVariable->valueText = *p++;
VariableUnserializer vu(staticVariable->valueText,
staticVariable->valueLen,
VariableUnserializer::Serialize);
try {
staticVariable->setStaticValue(vu.unserialize());
} catch (Exception &e) {
assert(false);
}
staticVariables.push_back(staticVariable);
}
p++;
ClassInfo::ReadUserAttributes(p, userAttrs);
}
p++;
}
ClassInfoUnique::ClassInfoUnique(const char **&p) {
m_attribute = (Attribute)(int64_t)(*p++);
assert(!(m_attribute & IsRedeclared));
// ClassInfoUnique is only created by ClassInfo::Load(), which is called
// from hphp_process_init() in the thread-neutral initialization phase.
// It is OK to create StaticStrings here, and throw the smart ptrs away,
// because the underlying static StringData will not be released.
m_name = makeStaticString(*p++);
m_parent = makeStaticString(*p++);
m_parentInfo = 0;
m_file = *p++;
m_line1 = (int)(int64_t)(*p++);
m_line2 = (int)(int64_t)(*p++);
if (m_attribute & IsVolatile) {
m_cdec_offset = (int)(int64_t)(*p++);
}
if (m_attribute & HasDocComment) {
m_docComment = *p++;
}
while (*p) {
String iface_name = makeStaticString(*p++);
assert(m_interfaces.find(iface_name) == m_interfaces.end());
m_interfaces.insert(iface_name);
m_interfacesVec.push_back(iface_name);
}
p++;
if (m_attribute & ClassInfo::UsesTraits) {
while (*p) {
String trait_name = makeStaticString(*p++);
assert(m_traits.find(trait_name) == m_traits.end());
m_traits.insert(trait_name);
m_traitsVec.push_back(trait_name);
}
p++;
}
if (m_attribute & ClassInfo::HasAliasedMethods) {
while (*p) {
String new_name = makeStaticString(*p++);
String old_name = makeStaticString(*p++);
m_traitAliasesVec.push_back(std::pair<String, String>(
new_name, old_name));
}
p++;
}
while (*p) {
MethodInfo *method = new MethodInfo(p);
assert(m_methods.find(method->name) == m_methods.end());
m_methods[method->name] = method;
m_methodsVec.push_back(method);
}
p++;
while (*p) {
PropertyInfo *property = new PropertyInfo();
property->attribute = (Attribute)(int64_t)(*p++);
property->name = makeStaticString(*p++);
property->type = DataType((int)uintptr_t(*p++));
property->owner = this;
assert(m_properties.find(property->name) == m_properties.end());
m_properties[property->name] = property;
m_propertiesVec.push_back(property);
}
p++;
while (*p) {
ConstantInfo *constant = new ConstantInfo();
constant->name = makeStaticString(*p++);
const char *len_or_cw = *p++;
constant->valueText = *p++;
if (uintptr_t(constant->valueText) > 0x100) {
constant->valueLen = (int64_t)len_or_cw;
VariableUnserializer vu(constant->valueText,
constant->valueLen,
VariableUnserializer::Serialize);
try {
constant->setStaticValue(vu.unserialize());
} catch (Exception &e) {
assert(false);
}
} else if (constant->valueText) {
DataType dt = DataType((int)uintptr_t(constant->valueText) - 2);
constant->valueLen = 0;
constant->valueText = nullptr;
Variant v;
if (dt == KindOfUnknown) {
constant->valueLen = intptr_t(len_or_cw);
} else {
v = ClassInfo::GetVariant(dt, len_or_cw);
constant->setStaticValue(v);
}
} else {
constant->callback = (void*)len_or_cw;
}
assert(m_constants.find(constant->name) == m_constants.end());
m_constants[constant->name] = constant;
m_constantsVec.push_back(constant);
}
p++;
while (*p) {
UserAttributeInfo *userAttr = new UserAttributeInfo();
userAttr->name = makeStaticString(*p++);
const char *len = *p++;
const char *valueText = *p++;
int64_t valueLen = (int64_t)len;
VariableUnserializer vu(valueText,
valueLen,
VariableUnserializer::Serialize);
userAttr->setStaticValue(vu.unserialize());
m_userAttrVec.push_back(userAttr);
}
p++;
}
ClassInfoUnique::~ClassInfoUnique() {
for (auto it = m_userAttrVec.begin(); it != m_userAttrVec.end(); ++it) {
delete *it;
}
}
const ClassInfo *ClassInfoUnique::getParentClassInfo() const {
if (m_parentInfo) return m_parentInfo;
if (m_parent.empty()) return nullptr;
return FindClass(m_parent);
}
void ClassInfoUnique::postInit() {
if (m_parent.empty()) return;
const ClassInfo *ci = FindClassInterfaceOrTrait(m_parent);
if (!ci) return;
if ((m_attribute & IsInterface) !=
(ci->getAttribute() & (IsInterface|IsTrait|IsRedeclared))) {
return;
}
m_parentInfo = ci;
}
void ClassInfo::Load() {
if (s_loaded) return;
const char **p = g_class_map;
while (*p) {
Attribute attribute = (Attribute)(int64_t)*p;
always_assert(!(attribute & IsRedeclared));
ClassInfo *info = new ClassInfoUnique(p);
if (info->m_name.empty()) {
if (attribute & IsSystem) {
assert(s_systemFuncs == nullptr);
s_systemFuncs = info;
} else {
always_assert(false);
}
} else {
ClassInfo *&i = s_class_like[info->m_name];
assert(!i);
i = info;
}
}
assert(s_systemFuncs);
s_loaded = true;
for (ClassMap::iterator it = s_class_like.begin(), end = s_class_like.end();
it != end; ++it) {
it->second->postInit();
}
}
void ClassInfo::postInit() {}
ClassInfo::ParameterInfo::~ParameterInfo() {
for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
delete *it;
}
}
ClassInfo::MethodInfo::~MethodInfo() {
if (attribute & ClassInfo::IsRedeclared) {
for (vector<const ParameterInfo *>::iterator it = parameters.begin();
it != parameters.end(); ++it) {
delete (MethodInfo*)(void*)*it;
}
} else {
for (vector<const ParameterInfo *>::iterator it = parameters.begin();
it != parameters.end(); ++it) {
delete *it;
}
for (vector<const ConstantInfo *>::iterator it = staticVariables.begin();
it != staticVariables.end(); ++it) {
delete *it;
}
}
for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
delete *it;
}
}
///////////////////////////////////////////////////////////////////////////////
}