363d1bb20f
This change is mostly for FB internal organizational reasons. Building is not effected beyond the fact that the target now lands in hphp/hhvm/hhvm rather than src/hhvm/hhvm.
1401 linhas
42 KiB
C++
1401 linhas
42 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 = NULL;
|
|
ClassInfo *ClassInfo::s_userFuncs = NULL;
|
|
ClassInfo::ClassMap ClassInfo::s_class_like;
|
|
ClassInfoHook *ClassInfo::s_hook = NULL;
|
|
|
|
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_userFuncs) {
|
|
const MethodVec &methods = s_userFuncs->getMethodsVec();
|
|
for (unsigned i = 0; i < methods.size(); i++) {
|
|
ret.append(methods[i]->name);
|
|
}
|
|
}
|
|
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 == NULL && s_hook) {
|
|
ret = s_hook->findFunction(name);
|
|
}
|
|
if (ret == NULL) {
|
|
ret = s_userFuncs->getMethodInfo(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;
|
|
}
|
|
|
|
const ClassInfo::ConstantInfo *ClassInfo::FindConstant(CStrRef name) {
|
|
assert(!name.isNull());
|
|
assert(s_loaded);
|
|
const ConstantInfo *info;
|
|
info = s_systemFuncs->getConstantInfo(name);
|
|
if (info) return info;
|
|
if (s_hook) {
|
|
info = s_hook->findConstant(name);
|
|
if (info) return info;
|
|
}
|
|
info = s_userFuncs->getConstantInfo(name);
|
|
return info;
|
|
}
|
|
|
|
ClassInfo::ConstantInfo::ConstantInfo() : callbacks(NULL), deferred(true) {
|
|
}
|
|
|
|
Variant ClassInfo::ConstantInfo::getValue() const {
|
|
if (deferred) {
|
|
if (callbacks == NULL) {
|
|
return get_constant(name);
|
|
}
|
|
return callbacks->os_constant(name);
|
|
}
|
|
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;
|
|
}
|
|
|
|
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->valueLen) {
|
|
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());
|
|
}
|
|
}
|
|
{
|
|
const ConstantMap &ucm = s_userFuncs->getConstants();
|
|
for (ConstantMap::const_iterator it = ucm.begin(); it != ucm.end(); ++it) {
|
|
res.set(it->first, it->second->getValue());
|
|
}
|
|
}
|
|
if (s_hook) {
|
|
dyn = s_hook->getConstants();
|
|
if (!dyn.isNull()) {
|
|
res.merge(dyn);
|
|
}
|
|
}
|
|
dyn = get_globals()->getDynamicConstants();
|
|
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 = NULL;
|
|
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::GetClassProperties(PropertyMap &props, CStrRef classname) {
|
|
if (!classname.empty()) {
|
|
const ClassInfo *classInfo = FindClass(classname);
|
|
if (classInfo) {
|
|
classInfo->getAllProperties(props);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassInfo::GetClassProperties(PropertyVec &props, CStrRef classname) {
|
|
if (!classname.empty()) {
|
|
const ClassInfo *classInfo = FindClass(classname);
|
|
if (classInfo) {
|
|
classInfo->getAllProperties(props);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (!hhvm) {
|
|
GetClassSymbolNames(GetTraits(), false, true, 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_globals() + m_cdec_offset) ? this : 0;
|
|
} else {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
const ClassInfo *ClassInfo::getParentClassInfo() const {
|
|
CStrRef parentName = getParentClass();
|
|
if (parentName.empty()) return NULL;
|
|
return FindClass(parentName);
|
|
}
|
|
|
|
void ClassInfo::getAllParentsVec(ClassVec &parents) const {
|
|
CStrRef parent = getParentClass();
|
|
if (!parent.empty()) {
|
|
parents.push_back(parent);
|
|
const ClassInfo *info = FindClass(parent);
|
|
if (info) info->getAllParentsVec(parents);
|
|
}
|
|
}
|
|
|
|
void ClassInfo::getAllInterfacesVec(InterfaceVec &interfaces) const {
|
|
CStrRef parent = getParentClass();
|
|
if (!parent.empty()) {
|
|
const ClassInfo *info = FindClass(parent);
|
|
if (info) info->getAllInterfacesVec(interfaces);
|
|
}
|
|
|
|
const InterfaceVec &ifs = getInterfacesVec();
|
|
for (unsigned int i = 0; i < ifs.size(); i++) {
|
|
CStrRef intf = ifs[i];
|
|
interfaces.push_back(intf);
|
|
const ClassInfo *info = FindInterface(intf);
|
|
if (info) info->getAllInterfacesVec(interfaces);
|
|
}
|
|
}
|
|
|
|
bool ClassInfo::derivesFrom(CStrRef name, bool considerInterface) const {
|
|
assert(!name.isNull());
|
|
return derivesFromImpl(name, considerInterface);
|
|
}
|
|
|
|
bool ClassInfo::derivesFromImpl(CStrRef name, bool considerInterface) const {
|
|
if (name->isame(getParentClass().get())) {
|
|
return true;
|
|
}
|
|
|
|
const ClassInfo *parent = getParentClassInfo();
|
|
if (parent && parent->derivesFromImpl(name, considerInterface)) {
|
|
return true;
|
|
}
|
|
|
|
if (considerInterface) {
|
|
const InterfaceSet &interfaces = getInterfaces();
|
|
for (InterfaceSet::const_iterator iter = interfaces.begin();
|
|
iter != interfaces.end(); ++iter) {
|
|
if (name->isame(iter->get())) {
|
|
return true;
|
|
}
|
|
const ClassInfo *parent = FindInterface(*iter);
|
|
if (parent && parent->derivesFromImpl(name, considerInterface)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClassInfo::IsSubClass(CStrRef className1, CStrRef className2,
|
|
bool considerInterface) {
|
|
const ClassInfo *clsInfo1 = ClassInfo::FindClass(className1);
|
|
if (!clsInfo1) return false;
|
|
|
|
return clsInfo1->derivesFrom(className2, considerInterface);
|
|
}
|
|
|
|
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 NULL;
|
|
}
|
|
|
|
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 = NULL;
|
|
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 NULL;
|
|
}
|
|
|
|
// internal function className::methodName or callObject->methodName
|
|
bool ClassInfo::HasAccess(CStrRef className, CStrRef methodName,
|
|
bool staticCall, bool hasCallObject) {
|
|
// It has to be either a static call or a call with an object.
|
|
assert(staticCall || hasCallObject);
|
|
const ClassInfo *clsInfo = ClassInfo::FindClass(className);
|
|
if (!clsInfo) return false;
|
|
ClassInfo *defClass;
|
|
ClassInfo::MethodInfo *methodInfo =
|
|
clsInfo->hasMethod(methodName, defClass);
|
|
if (!methodInfo) return false;
|
|
if (methodInfo->attribute & ClassInfo::IsPublic) return true;
|
|
CStrRef ctxName = hhvm
|
|
? g_vmContext->getContextClassName()
|
|
: FrameInjection::GetClassName(true);
|
|
if (ctxName->size() == 0) {
|
|
return false;
|
|
}
|
|
const ClassInfo *ctxClass = ClassInfo::FindClass(ctxName);
|
|
bool hasObject = hasCallObject ||
|
|
(hhvm ? g_vmContext->getThis() : FrameInjection::GetThis(true));
|
|
if (ctxClass) {
|
|
return ctxClass->checkAccess(defClass, methodInfo, staticCall, hasObject);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClassInfo::checkAccess(ClassInfo *defClass,
|
|
MethodInfo *methodInfo,
|
|
bool staticCall,
|
|
bool hasObject) const {
|
|
assert(defClass && methodInfo);
|
|
if ((m_name->isame(defClass->m_name.get()))) {
|
|
if (methodInfo->attribute & ClassInfo::IsStatic) return true;
|
|
return hasObject;
|
|
}
|
|
|
|
if (methodInfo->attribute & ClassInfo::IsStatic) {
|
|
if (methodInfo->attribute & ClassInfo::IsPublic) return true;
|
|
if (methodInfo->attribute & ClassInfo::IsProtected) {
|
|
return derivesFrom(defClass->m_name, false) ||
|
|
defClass->derivesFrom(m_name, false);
|
|
}
|
|
return false;
|
|
}
|
|
if (!hasObject && !staticCall) return false;
|
|
if (methodInfo->attribute & ClassInfo::IsPublic) return true;
|
|
if (methodInfo->attribute & ClassInfo::IsProtected) {
|
|
return derivesFrom(defClass->m_name, false) ||
|
|
defClass->derivesFrom(m_name, false);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *ClassInfo::getConstructor() const {
|
|
ClassInfo *defClass;
|
|
if (hasMethod("__construct", defClass)) {
|
|
return "__construct";
|
|
}
|
|
if (!(m_attribute & IsTrait) && hasMethod(m_name, defClass)) {
|
|
return m_name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void ClassInfo::getAllProperties(PropertyMap &props) const {
|
|
const PropertyMap &properties = getProperties();
|
|
props.insert(properties.begin(), properties.end());
|
|
|
|
CStrRef parentClass = getParentClass();
|
|
if (!parentClass.empty()) {
|
|
GetClassProperties(props, parentClass);
|
|
}
|
|
}
|
|
|
|
void ClassInfo::getAllProperties(PropertyVec &props) const {
|
|
const PropertyVec &properties = getPropertiesVec();
|
|
props.insert(props.end(), properties.begin(), properties.end());
|
|
|
|
CStrRef parentClass = getParentClass();
|
|
if (!parentClass.empty()) {
|
|
GetClassProperties(props, parentClass);
|
|
}
|
|
}
|
|
|
|
void ClassInfo::filterProperties(Array &props, Attribute toRemove) const {
|
|
const PropertyVec &properties = getPropertiesVec();
|
|
for (unsigned i = 0; i < properties.size(); i++) {
|
|
if (properties[i]->attribute & toRemove) {
|
|
props.remove(properties[i]->name, true);
|
|
}
|
|
}
|
|
const ClassInfo *parent = getParentClassInfo();
|
|
if (parent) {
|
|
parent->filterProperties(props, toRemove);
|
|
}
|
|
}
|
|
|
|
ClassInfo::PropertyInfo *ClassInfo::getPropertyInfo(CStrRef name) const {
|
|
assert(!name.isNull());
|
|
const PropertyMap &properties = getProperties();
|
|
PropertyMap::const_iterator iter = properties.find(name);
|
|
if (iter != properties.end()) {
|
|
return iter->second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool ClassInfo::hasProperty(CStrRef name) const {
|
|
assert(!name.isNull());
|
|
const PropertyMap &properties = getProperties();
|
|
return properties.find(name) != properties.end();
|
|
}
|
|
|
|
ClassInfo::ConstantInfo *ClassInfo::getConstantInfo(CStrRef name) const {
|
|
assert(!name.isNull());
|
|
const ConstantMap &constants = getConstants();
|
|
ConstantMap::const_iterator iter = constants.find(name);
|
|
if (iter != constants.end()) {
|
|
return iter->second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool ClassInfo::hasConstant(CStrRef name) const {
|
|
assert(!name.isNull());
|
|
const ConstantMap &constants = getConstants();
|
|
return constants.find(name) != constants.end();
|
|
}
|
|
|
|
bool ClassInfo::PropertyInfo::isVisible(const ClassInfo *context) const {
|
|
if ((attribute & ClassInfo::IsPublic) || context == owner) return true;
|
|
if (!context) return false;
|
|
if (attribute & ClassInfo::IsProtected) {
|
|
return owner->derivesFrom(context->getName(), false) ||
|
|
context->derivesFrom(owner->getName(), false);
|
|
}
|
|
assert(attribute & ClassInfo::IsPrivate);
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// load functions
|
|
|
|
static String makeStaticString(const char *s) {
|
|
if (!s) {
|
|
return null_string;
|
|
}
|
|
if (has_eval_support) {
|
|
// hhvm uses GetStaticString
|
|
return StringData::GetStaticString(s);
|
|
}
|
|
// binaries compiled with hphpc don't use GetStaticString(), and
|
|
// instead they use TheStaticStringSet; see "type_string.cpp" for
|
|
// details
|
|
StringData* sd = new StringData(s, AttachLiteral);
|
|
sd->setStatic();
|
|
StringDataSet& set = StaticString::TheStaticStringSet();
|
|
StringDataSet::iterator it = set.find(sd);
|
|
if (it != set.end()) {
|
|
delete sd;
|
|
return *it;
|
|
}
|
|
set.insert(sd);
|
|
return sd;
|
|
}
|
|
|
|
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 valueLen = (int64)len;
|
|
VariableUnserializer vu(valueText,
|
|
valueLen,
|
|
VariableUnserializer::Serialize);
|
|
userAttr->setStaticValue(vu.unserialize());
|
|
|
|
userAttrVec.push_back(userAttr);
|
|
}
|
|
}
|
|
|
|
ClassInfo::MethodInfo *ClassInfo::MethodInfo::getDeclared() {
|
|
if (attribute & ClassInfo::IsRedeclared) {
|
|
RedeclaredCallInfo *ci = *(RedeclaredCallInfo**)((char*)get_globals() +
|
|
volatile_redec_offset);
|
|
if (!ci) return 0;
|
|
return (ClassInfo::MethodInfo*)(void*)parameters[ci->redeclaredId];
|
|
} else if (attribute & ClassInfo::IsVolatile) {
|
|
return *(bool*)((char*)get_globals() + volatile_redec_offset) ? this : 0;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
ClassInfo::MethodInfo::MethodInfo(const char **&p) {
|
|
attribute = (Attribute)(int64)(*p++);
|
|
name = makeStaticString(*p++);
|
|
docComment = 0;
|
|
if (attribute & ClassInfo::IsRedeclared) {
|
|
volatile_redec_offset = (int)(int64)(*p++);
|
|
while (*p) {
|
|
MethodInfo *m = new MethodInfo(p);
|
|
parameters.push_back((ParameterInfo*)(void*)m);
|
|
}
|
|
} else {
|
|
file = *p++;
|
|
line1 = (int)(int64)(*p++);
|
|
line2 = (int)(int64)(*p++);
|
|
if (attribute & IsVolatile) {
|
|
volatile_redec_offset = (int)(int64)(*p++);
|
|
}
|
|
|
|
if (attribute & HasDocComment) {
|
|
docComment = *p++;
|
|
}
|
|
|
|
if (attribute & IsSystem) {
|
|
returnType = (DataType)(int64)(*p++);
|
|
}
|
|
while (*p) {
|
|
ParameterInfo *parameter = new ParameterInfo();
|
|
parameter->attribute = (Attribute)(int64)(*p++);
|
|
parameter->name = *p++;
|
|
parameter->type = *p++;
|
|
if (attribute & IsSystem) {
|
|
parameter->argType = (DataType)(int64)(*p++);
|
|
}
|
|
parameter->value = *p++;
|
|
parameter->valueText = *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)(*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)(*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)(*p++);
|
|
m_line2 = (int)(int64)(*p++);
|
|
|
|
if (m_attribute & IsVolatile) {
|
|
m_cdec_offset = (int)(int64)(*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)(*p++);
|
|
property->name = makeStaticString(*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 (constant->valueText) {
|
|
constant->valueLen = (int64)len_or_cw;
|
|
VariableUnserializer vu(constant->valueText,
|
|
constant->valueLen,
|
|
VariableUnserializer::Serialize);
|
|
try {
|
|
constant->setStaticValue(vu.unserialize());
|
|
} catch (Exception &e) {
|
|
assert(false);
|
|
}
|
|
} else {
|
|
constant->valueLen = 0;
|
|
if (!m_name.empty()) {
|
|
const ObjectStaticCallbacks *cwo =
|
|
(const ObjectStaticCallbacks*)len_or_cw;
|
|
|
|
assert(cwo);
|
|
constant->callbacks = cwo;
|
|
}
|
|
}
|
|
|
|
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 valueLen = (int64)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 NULL;
|
|
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;
|
|
}
|
|
|
|
ClassInfoRedeclared::ClassInfoRedeclared(const char **&p) {
|
|
m_attribute = (Attribute)(int64)(*p++);
|
|
m_name = makeStaticString(*p++);
|
|
m_redeclaredIdOffset = (int)(int64)*p++;
|
|
while (*p) {
|
|
ClassInfo *cls = new ClassInfoUnique(p);
|
|
m_redeclaredClasses.push_back(cls);
|
|
}
|
|
p++;
|
|
}
|
|
|
|
const ClassInfo *ClassInfoRedeclared::getCurrentOrNull() const {
|
|
const RedeclaredObjectStaticCallbacks *rosc =
|
|
*(const RedeclaredObjectStaticCallbacks**)((char*)get_globals() +
|
|
m_redeclaredIdOffset);
|
|
int id = rosc->getRedeclaringId();
|
|
if (LIKELY(id >= 0)) {
|
|
return m_redeclaredClasses[id];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ClassInfoRedeclared::postInit() {
|
|
for (int i = m_redeclaredClasses.size(); i--; ) {
|
|
m_redeclaredClasses[i]->postInit();
|
|
}
|
|
}
|
|
|
|
void ClassInfo::Load() {
|
|
assert(!s_loaded);
|
|
const char **p = g_class_map;
|
|
while (*p) {
|
|
Attribute attribute = (Attribute)(int64)*p;
|
|
ClassInfo *info = (attribute & IsRedeclared) ?
|
|
static_cast<ClassInfo*>(new ClassInfoRedeclared(p)) :
|
|
static_cast<ClassInfo*>(new ClassInfoUnique(p));
|
|
|
|
if (info->m_name.empty()) {
|
|
if (attribute & IsSystem) {
|
|
assert(s_systemFuncs == NULL);
|
|
s_systemFuncs = info;
|
|
} else {
|
|
assert(s_userFuncs == NULL);
|
|
s_userFuncs = info;
|
|
}
|
|
} else {
|
|
ClassInfo *&i = s_class_like[info->m_name];
|
|
assert(!i);
|
|
i = info;
|
|
}
|
|
}
|
|
|
|
assert(s_systemFuncs);
|
|
assert(s_userFuncs);
|
|
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;
|
|
}
|
|
}
|
|
|
|
Variant ClassPropTable::getInitVal(const ClassPropTableEntry *prop) const {
|
|
int64 id = m_static_inits[prop->init_offset];
|
|
if (LIKELY(!(id & 7))) {
|
|
return *(Variant*)id;
|
|
}
|
|
switch (id & 7) {
|
|
case 1: {
|
|
int off = id >> 32;
|
|
CStrRef s = getInitS((id>>4) & 0xfffffff);
|
|
if (off) {
|
|
char *addr = (char*)get_global_variables() + off;
|
|
return getDynamicConstant(*(Variant*)addr, s);
|
|
} else {
|
|
return getUndefinedConstant(s);
|
|
}
|
|
}
|
|
case 2: {
|
|
const ObjectStaticCallbacks *osc =
|
|
(const ObjectStaticCallbacks *)getInitP((id >> 4) & 0xfffffff);
|
|
const char *addr =
|
|
(const char *)osc->lazy_initializer(get_global_variables()) +
|
|
(id >> 32);
|
|
return *(Variant*)addr;
|
|
}
|
|
case 3: {
|
|
char *addr = (char*)get_global_variables() + (id & 0x7ffffff8);
|
|
ObjectStaticCallbacks *osc = *(ObjectStaticCallbacks**)addr;
|
|
return osc->os_constant(getInitS(id>>32).c_str());
|
|
}
|
|
case 4: {
|
|
ObjectStaticCallbacks *osc =
|
|
(ObjectStaticCallbacks*)getInitP((id & 0x7fffffff)>>4);
|
|
return osc->os_constant(getInitS(id>>32).c_str());
|
|
}
|
|
case 5:
|
|
throw FatalErrorException(0, "unknown class constant %s::%s",
|
|
getInitS((id & 0x7fffffff)>>4).c_str(),
|
|
getInitS(id>>32).c_str());
|
|
|
|
case 6: {
|
|
void *func = getInitP((id >> 4) & 0x7ffffff);
|
|
if (id >> 32) {
|
|
return ((CVarRef (*)())func)();
|
|
} else {
|
|
return ((Variant (*)())func)();
|
|
}
|
|
}
|
|
|
|
case 7:
|
|
return
|
|
ClassPropTableEntry::GetVariant(DataType((id >> 4) & kDataTypeMask),
|
|
getInitP(id >> 32));
|
|
}
|
|
throw FatalErrorException("Failed to get init val");
|
|
}
|
|
|
|
static const ClassPropTableEntry *FindRedeclaredProp(
|
|
const ObjectData *&obj, const ClassPropTableEntry *p, int &flags) {
|
|
const ObjectStaticCallbacks *osc = obj->o_get_callbacks();
|
|
const char *globals = 0;
|
|
assert(osc);
|
|
const ClassPropTable *cpt = osc->cpt->m_parent;
|
|
|
|
while (true) {
|
|
while (cpt) {
|
|
if (cpt->m_size_mask >= 0) {
|
|
const int *ix = cpt->m_hash_entries;
|
|
int h = p->hash & cpt->m_size_mask;
|
|
int o = ix[h];
|
|
if (o >= 0) {
|
|
const ClassPropTableEntry *prop = cpt->m_entries + o;
|
|
do {
|
|
if (p->hash != prop->hash ||
|
|
prop->isPrivate()) {
|
|
continue;
|
|
}
|
|
|
|
if (LIKELY(!strcmp(prop->keyName->data() + prop->prop_offset,
|
|
p->keyName->data() + p->prop_offset))) {
|
|
if (prop->isOverride() && !prop->offset) {
|
|
flags |= prop->flags;
|
|
continue;
|
|
}
|
|
assert(prop->type == KindOfUnknown);
|
|
return prop;
|
|
}
|
|
} while (!prop++->isLast());
|
|
}
|
|
}
|
|
cpt = cpt->m_parent;
|
|
}
|
|
if (LIKELY(!osc->redeclaredParent)) break;
|
|
if (LIKELY(!globals)) {
|
|
globals = (char*)get_global_variables();
|
|
}
|
|
osc = *(ObjectStaticCallbacks**)(globals + osc->redeclaredParent);
|
|
obj = obj->getRedeclaredParent();
|
|
cpt = osc->cpt;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ClassInfo::GetArray(const ObjectData *obj, const ClassPropTable *ct,
|
|
Array &props, GetArrayKind kind) {
|
|
if (ct) {
|
|
Array done;
|
|
const ObjectData *base = 0;
|
|
do {
|
|
const ClassPropTableEntry *p = ct->m_entries;
|
|
int off = ct->m_offset;
|
|
if (off >= 0) {
|
|
do {
|
|
p += off;
|
|
if ((kind & GetArrayPrivate) || p->isPublic()) {
|
|
if (done.get() && done.get()->exists(p->offset)) continue;
|
|
if (p->isOverride()) {
|
|
/* The actual property is stored in a base class.
|
|
It might have been promoted from protected,
|
|
so mark here that the prop does not need to be
|
|
set again.
|
|
*/
|
|
if (UNLIKELY(!p->offset)) {
|
|
/*
|
|
Its stored in a redeclared base. We have to find
|
|
the value, and update it now.
|
|
*/
|
|
const ObjectData *parent = obj;
|
|
int flags = 0;
|
|
if (const ClassPropTableEntry *pp =
|
|
FindRedeclaredProp(parent, p, flags)) {
|
|
|
|
if (done.get() && done.get()->exists(pp->offset)) continue;
|
|
const char *addr = ((const char *)parent) + pp->offset;
|
|
|
|
props.lvalAt(*p->keyName, AccessFlags::Key)
|
|
.setWithRef(*(Variant*)addr);
|
|
|
|
done.set(pp->offset, true_varNR);
|
|
continue;
|
|
}
|
|
/*
|
|
Its a dynamic property. If this is a protected prop,
|
|
need to prevent it being added again when we deal
|
|
with dynamic props. Otherwise, we just insert it now
|
|
(to get the order right), and let it get set later.
|
|
*/
|
|
if (p->isPublic() &&
|
|
!(flags & ClassPropTableEntry::Protected)) {
|
|
props.set(*p->keyName, null_variant, true);
|
|
} else {
|
|
CArrRef oprop = parent->getProperties();
|
|
if (oprop.get()) {
|
|
String name(p->keyName->data() + p->prop_offset,
|
|
p->keyName->size() - p->prop_offset,
|
|
AttachLiteral);
|
|
if (done.get() && done.get()->exists(name)) continue;
|
|
|
|
CVarRef val = oprop.get()->get(name);
|
|
if (val.isInitialized()) {
|
|
props.lvalAt(*p->keyName, AccessFlags::Key)
|
|
.setWithRef(val);
|
|
base = parent;
|
|
done.set(name, false_varNR);
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
done.set(p->offset, true_varNR);
|
|
}
|
|
const char *addr = ((const char *)obj) + p->offset;
|
|
if (LIKELY(p->type == KindOfUnknown)) {
|
|
if (isInitialized(*(Variant*)addr)) {
|
|
props.lvalAt(*p->keyName, AccessFlags::Key)
|
|
.setWithRef(*(Variant*)addr);
|
|
}
|
|
continue;
|
|
}
|
|
Variant v = p->getVariant(addr);
|
|
if (p->isPrivate()) {
|
|
props.add(*p->keyName, v, true);
|
|
} else {
|
|
props.set(*p->keyName, v, true);
|
|
}
|
|
}
|
|
} while ((off = p->next) != 0);
|
|
}
|
|
ct = ct->m_parent;
|
|
if (!ct) {
|
|
ObjectData *parent = obj->getRedeclaredParent();
|
|
if (parent) {
|
|
assert(parent != obj);
|
|
obj = parent;
|
|
ct = obj->o_getClassPropTable();
|
|
}
|
|
}
|
|
} while (ct);
|
|
if (base) {
|
|
if (LIKELY(kind & GetArrayDynamic)) {
|
|
for (ArrayIter it(base->getProperties()); !it.end(); it.next()) {
|
|
Variant key = it.first();
|
|
if (!done.get()->exists(key)) {
|
|
CVarRef value = it.secondRef();
|
|
props.lvalAt(key, AccessFlags::Key).setWithRef(value);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (LIKELY(kind & GetArrayDynamic)) {
|
|
if (hhvm) {
|
|
HPHP::VM::Instance *inst = static_cast<HPHP::VM::Instance*>(
|
|
const_cast<ObjectData*>(obj));
|
|
inst->HPHP::VM::Instance::o_getArray(props, !(kind & GetArrayPrivate));
|
|
} else {
|
|
obj->o_getArray(props, !(kind & GetArrayPrivate));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassInfo::SetArray(ObjectData *obj, const ClassPropTable *ct,
|
|
CArrRef props) {
|
|
while (ct) {
|
|
for (const int *ppi = ct->privates(); *ppi >= 0; ppi++) {
|
|
const ClassPropTableEntry *p = ct->m_entries + *ppi;
|
|
assert(!p->isPublic());
|
|
CVarRef value = props->get(*p->keyName);
|
|
if (!value.isInitialized()) continue;
|
|
const char *addr = ((const char *)obj) + p->offset;
|
|
if (UNLIKELY(!p->offset)) {
|
|
/*
|
|
Its stored in a redeclared base. We have to find
|
|
the value, and update it now.
|
|
*/
|
|
const ObjectData *parent = obj;
|
|
int flags = 0;
|
|
if (const ClassPropTableEntry *pp =
|
|
FindRedeclaredProp(parent, p, flags)) {
|
|
|
|
p = pp;
|
|
addr = ((const char *)parent) + pp->offset;
|
|
} else {
|
|
String name(p->keyName->data() + p->prop_offset,
|
|
p->keyName->size() - p->prop_offset,
|
|
AttachLiteral);
|
|
Variant *val = parent->ObjectData::o_realPropHook(
|
|
name, ObjectData::RealPropCreate|ObjectData::RealPropWrite);
|
|
val->setWithRef(value);
|
|
continue;
|
|
}
|
|
}
|
|
if (LIKELY(p->type == KindOfUnknown)) {
|
|
((Variant*)addr)->setWithRef(value);
|
|
continue;
|
|
}
|
|
switch (p->type) {
|
|
case KindOfBoolean: *(bool*)addr = value; break;
|
|
case KindOfInt64: *(int64*)addr = value; break;
|
|
case KindOfDouble: *(double*)addr = value; break;
|
|
case KindOfString:
|
|
*(String*)addr = value.isString() ? value.getStringData() : NULL;
|
|
break;
|
|
case KindOfArray:
|
|
*(Array*)addr = value.isArray() ? value.getArrayData() : NULL;
|
|
break;
|
|
case KindOfObject:
|
|
*(Object*)addr = value.isObject() ? value.getObjectData() : NULL;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
ct = ct->m_parent;
|
|
if (!ct) {
|
|
ObjectData *parent = obj->getRedeclaredParent();
|
|
if (parent) {
|
|
assert(parent != obj);
|
|
obj = parent;
|
|
ct = obj->o_getClassPropTable();
|
|
}
|
|
}
|
|
}
|
|
if (hhvm) {
|
|
HPHP::VM::Instance *inst = static_cast<HPHP::VM::Instance*>(obj);
|
|
inst->HPHP::VM::Instance::o_setArray(props);
|
|
} else {
|
|
obj->o_setArray(props);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|