Arquivos
hhvm/hphp/runtime/ext/ext_reflection.cpp
T
Jordan DeLong e7ab1d6c48 Some cleanup to TypeConstraint, expose the DataType/MetaType fields
I need access to the DataType field, and accessing metaType()
directly is more convenient than going through the isFoo functions
(and if you use switch it also means we can get warnings when people
add new metatypes).  Some cleanup along the way (e.g. pull
equivDataTypes out of there, group related members a little more,
etc).

Reviewed By: @dariorussi

Differential Revision: D1014535
2013-10-17 20:27:24 -07:00

1046 linhas
31 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| 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 "hphp/runtime/ext/ext_reflection.h"
#include "hphp/runtime/ext/ext_closure.h"
#include "hphp/runtime/ext/ext_misc.h"
#include "hphp/runtime/ext/ext_string.h"
#include "hphp/runtime/base/externals.h"
#include "hphp/runtime/base/class-info.h"
#include "hphp/runtime/base/runtime-option.h"
#include "hphp/runtime/base/string-util.h"
#include "hphp/runtime/vm/jit/translator-inline.h"
#include "hphp/parser/parser.h"
#include "hphp/runtime/ext/util.h"
#include "hphp/system/systemlib.h"
namespace HPHP {
using Transl::VMRegAnchor;
IMPLEMENT_DEFAULT_EXTENSION(Reflection);
///////////////////////////////////////////////////////////////////////////////
const StaticString
s_name("name"),
s_version("version"),
s_info("info"),
s_ini("ini"),
s_constants("constants"),
s_constructor("constructor"),
s_functions("functions"),
s_classes("classes"),
s_access("access"),
s_public("public"),
s_protected("protected"),
s_private("private"),
s_file("file"),
s_line1("line1"),
s_line2("line2"),
s_doc("doc"),
s_modifiers("modifiers"),
s_class("class"),
s_ref("ref"),
s_index("index"),
s_type("type"),
s_nullable("nullable"),
s_msg("msg"),
s_default("default"),
s_defaultText("defaultText"),
s_params("params"),
s_final("final"),
s_abstract("abstract"),
s_internal("internal"),
s_is_closure("is_closure"),
s_is_generator("is_generator"),
s_hphp("hphp"),
s_static_variables("static_variables"),
s_extension("extension"),
s_interfaces("interfaces"),
s_traits("traits"),
s_interface("interface"),
s_trait("trait"),
s_methods("methods"),
s_properties("properties"),
s_private_properties("private_properties"),
s_attributes("attributes"),
s_function("function"),
s_trait_aliases("trait_aliases"),
s_varg("varg"),
s_closure("closure"),
s___invoke("__invoke"),
s_closure_in_braces("{closure}"),
s_closureobj("closureobj"),
s_return_type("return_type"),
s_type_hint("type_hint"),
s_accessible("accessible");
static const Class* get_cls(CVarRef class_or_object) {
Class* cls = nullptr;
if (class_or_object.is(KindOfObject)) {
ObjectData* obj = class_or_object.toCObjRef().get();
cls = obj->getVMClass();
} else {
cls = Unit::loadClass(class_or_object.toString().get());
}
return cls;
}
Array f_hphp_get_extension_info(const String& name) {
Array ret;
Extension *ext = Extension::GetExtension(name);
ret.set(s_name, name);
ret.set(s_version, ext ? ext->getVersion() : "");
ret.set(s_info, empty_string);
ret.set(s_ini, Array::Create());
ret.set(s_constants, Array::Create());
ret.set(s_functions, Array::Create());
ret.set(s_classes, Array::Create());
return ret;
}
int get_modifiers(int attribute, bool cls) {
int php_modifier = 0;
if (attribute & ClassInfo::IsAbstract) php_modifier |= cls ? 0x20 : 0x02;
if (attribute & ClassInfo::IsFinal) php_modifier |= cls ? 0x40 : 0x04;
if (attribute & ClassInfo::IsStatic) php_modifier |= 0x01;
if (attribute & ClassInfo::IsPublic) php_modifier |= 0x100;
if (attribute & ClassInfo::IsProtected) php_modifier |= 0x200;
if (attribute & ClassInfo::IsPrivate) php_modifier |= 0x400;
return php_modifier;
}
int get_modifiers(Attr attrs, bool cls) {
int php_modifier = 0;
if (attrs & AttrAbstract) php_modifier |= cls ? 0x20 : 0x02;
if (attrs & AttrFinal) php_modifier |= cls ? 0x40 : 0x04;
if (attrs & AttrStatic) php_modifier |= 0x01;
if (attrs & AttrPublic) php_modifier |= 0x100;
if (attrs & AttrProtected) php_modifier |= 0x200;
if (attrs & AttrPrivate) php_modifier |= 0x400;
return php_modifier;
}
static void set_attrs(Array& ret, int modifiers) {
if (modifiers & 0x100) {
ret.set(s_access, VarNR(s_public));
ret.set(s_accessible, true_varNR);
} else if (modifiers & 0x200) {
ret.set(s_access, VarNR(s_protected));
ret.set(s_accessible, false_varNR);
} else if (modifiers & 0x400) {
ret.set(s_access, VarNR(s_private));
ret.set(s_accessible, false_varNR);
} else {
assert(false);
}
ret.set(s_modifiers, VarNR(modifiers));
if (modifiers & 0x1) {
ret.set(s_static, true_varNR);
}
if (modifiers & 0x44) {
ret.set(s_final, true_varNR);
}
if (modifiers & 0x22) {
ret.set(s_abstract, true_varNR);
}
}
static bool set_source_info(Array &ret, const char *file, int line1,
int line2) {
if (!file) file = "";
if (file[0] != '/') {
ret.set(s_file, String(RuntimeOption::SourceRoot + file));
} else {
ret.set(s_file, file);
}
ret.set(s_line1, VarNR(line1));
ret.set(s_line2, VarNR(line2));
return file && *file;
}
static void set_doc_comment(Array &ret, const char *comment) {
if (comment) {
ret.set(s_doc, comment);
} else {
ret.set(s_doc, false_varNR);
}
}
static void set_doc_comment(Array &ret, const StringData* comment) {
if (comment && comment->size()) {
ret.set(s_doc, VarNR(comment));
} else {
ret.set(s_doc, false_varNR);
}
}
static void set_return_type_constraint(Array &ret, const StringData* retType) {
if (retType && retType->size()) {
ret.set(s_return_type, VarNR(retType));
} else {
ret.set(s_return_type, false_varNR);
}
}
static void set_property_info(Array &ret, ClassInfo::PropertyInfo *info,
const ClassInfo *cls) {
ret.set(s_name, info->name);
set_attrs(ret, get_modifiers(info->attribute, false) & ~0x66);
ret.set(s_class, VarNR(cls->getName()));
set_doc_comment(ret, info->docComment);
}
static void set_instance_prop_info(Array &ret, const Class::Prop* prop) {
ret.set(s_name, VarNR(prop->m_name));
set_attrs(ret, get_modifiers(prop->m_attrs, false) & ~0x66);
ret.set(s_class, VarNR(prop->m_class->name()));
set_doc_comment(ret, prop->m_docComment);
if (prop->m_typeConstraint && prop->m_typeConstraint->size()) {
ret.set(s_type, VarNR(prop->m_typeConstraint));
} else {
ret.set(s_type, false_varNR);
}
}
static void set_static_prop_info(Array &ret, const Class::SProp* prop) {
ret.set(s_name, VarNR(prop->m_name));
set_attrs(ret, get_modifiers(prop->m_attrs, false) & ~0x66);
ret.set(s_class, VarNR(prop->m_class->name()));
set_doc_comment(ret, prop->m_docComment);
if (prop->m_typeConstraint && prop->m_typeConstraint->size()) {
ret.set(s_type, VarNR(prop->m_typeConstraint));
} else {
ret.set(s_type, false_varNR);
}
}
static bool resolveConstant(const char *p, int64_t len, Variant &cns) {
// ltrim
while (len && (*p == ' ')) {
p++;
len--;
}
// rtrim
while (len && (p[len-1] == ' ')) {
len--;
}
String cname(p, len, CopyString);
if (!f_defined(cname)) {
cns = uninit_null();
return false;
}
cns = f_constant(cname);
return true;
}
static bool resolveDefaultParameterConstant(const char *value,
int64_t valueLen,
Variant &cns) {
const char *p = value;
const char *e = value + valueLen;
const char *s;
bool isLval = false;
int64_t lval = 0;
while ((s = strchr(p, '|'))) {
isLval = true;
if (!resolveConstant(p, s - p, cns)) {
return false;
}
lval |= cns.toInt64();
p = s + 1;
}
if (!resolveConstant(p, e - p, cns)) {
return false;
}
if (isLval) {
cns = cns.toInt64() | lval;
}
return true;
}
static void set_function_info(Array &ret, const ClassInfo::MethodInfo *info,
const String *classname) {
// return type
if (info->attribute & ClassInfo::IsReference) {
ret.set(s_ref, true_varNR);
}
if (info->attribute & ClassInfo::IsSystem) {
ret.set(s_internal, true_varNR);
}
if (info->attribute & ClassInfo::HipHopSpecific) {
ret.set(s_hphp, true_varNR);
}
if (info->attribute & ClassInfo::IsClosure) {
ret.set(s_is_closure, true_varNR);
}
if (info->attribute & ClassInfo::HasGeneratorAsBody) {
ret.set(s_is_generator, true_varNR);
}
// doc comments
set_doc_comment(ret, info->docComment);
// parameters
{
Array arr = Array::Create();
for (unsigned int i = 0; i < info->parameters.size(); i++) {
Array param = Array::Create();
const ClassInfo::ParameterInfo *p = info->parameters[i];
param.set(s_index, VarNR((int)i));
param.set(s_name, p->name);
param.set(s_type, p->type);
param.set(s_function, info->name);
if (classname) {
param.set(s_class, VarNR(*classname));
}
const char *defText = p->valueText;
int64_t defTextLen = p->valueTextLen;
if (defText == nullptr) {
defText = "";
defTextLen = 0;
}
if (!p->type || !*p->type || !strcasecmp("null", defText)) {
param.set(s_nullable, true_varNR);
}
if (p->value && *p->value) {
if (*p->value == '\x01') {
if ((defTextLen > 2) &&
!strcmp(defText + defTextLen - 2, "()")) {
const char *sep = strchr(defText, ':');
Object obj = SystemLib::AllocStdClassObject();
if (sep && sep[1] == ':') {
String cls = String(defText, sep - defText, CopyString);
String con = String(sep + 2, CopyString);
obj->o_set(s_class, cls);
obj->o_set(s_name, con);
} else {
obj->o_set(s_name, String(defText, defTextLen, CopyString));
}
param.set(s_default, Variant(obj));
} else {
Variant v;
if (resolveDefaultParameterConstant(defText, defTextLen, v)) {
param.set(s_default, v);
} else {
Object obj = SystemLib::AllocStdClassObject();
obj->o_set(s_msg, String("Unknown unserializable default value: ")
+ defText);
param.set(s_default, Variant(obj));
}
}
} else {
param.set(s_default, unserialize_from_string(p->value));
}
param.set(s_defaultText, defText);
}
if (p->attribute & ClassInfo::IsReference) {
param.set(s_ref, true_varNR);
}
{
Array userAttrs = Array::Create();
for (unsigned int i = 0; i < p->userAttrs.size(); ++i) {
const ClassInfo::UserAttributeInfo *ai = p->userAttrs[i];
userAttrs.set(ai->name, ai->getValue());
}
param.set(s_attributes, VarNR(userAttrs));
}
arr.append(param);
}
ret.set(s_params, arr);
}
// static variables
{
Array arr = Array::Create();
for (unsigned int i = 0; i < info->staticVariables.size(); i++) {
const ClassInfo::ConstantInfo *p = info->staticVariables[i];
assert(p->valueText && *p->valueText);
arr.set(p->name, p->valueText);
}
ret.set(s_static_variables, arr);
}
// user attributes
{
Array arr = Array::Create();
for (unsigned i = 0; i < info->userAttrs.size(); ++i) {
const ClassInfo::UserAttributeInfo *ai = info->userAttrs[i];
arr.set(ai->name, ai->getValue());
}
ret.set(s_attributes, VarNR(arr));
}
}
static void set_function_info(Array &ret, const Func* func) {
// return type
if (func->attrs() & AttrReference) {
ret.set(s_ref, true_varNR);
}
if (func->isBuiltin()) {
ret.set(s_internal, true_varNR);
if (func->info() &&
func->info()->attribute & ClassInfo::HipHopSpecific) {
ret.set(s_hphp, true_varNR);
}
}
set_return_type_constraint(ret, func->returnTypeConstraint());
// doc comments
set_doc_comment(ret, func->docComment());
// parameters
{
Array arr = Array::Create();
const Func::ParamInfoVec& params = func->params();
for (int i = 0; i < func->numParams(); i++) {
Array param = Array::Create();
const Func::ParamInfo& fpi = params[i];
param.set(s_index, VarNR((int)i));
VarNR name(func->localNames()[i]);
param.set(s_name, name);
auto const nonExtendedConstraint =
fpi.typeConstraint().hasConstraint() &&
!fpi.typeConstraint().isExtended();
auto const type = nonExtendedConstraint ? fpi.typeConstraint().typeName()
: empty_string.get();
param.set(s_type, VarNR(type));
const StringData* typeHint = fpi.userType() ?
fpi.userType() : empty_string.get();
param.set(s_type_hint, VarNR(typeHint));
param.set(s_function, VarNR(func->name()));
if (func->preClass()) {
param.set(s_class, VarNR(func->cls() ? func->cls()->name() :
func->preClass()->name()));
}
if (!nonExtendedConstraint || fpi.typeConstraint().isNullable()) {
param.set(s_nullable, true_varNR);
}
if (fpi.phpCode()) {
assert(fpi.hasDefaultValue());
if (fpi.hasScalarDefaultValue()) {
// Most of the time the default value is scalar, so we can
// avoid evaling in the common case
param.set(s_default, tvAsVariant((TypedValue*)&fpi.defaultValue()));
} else {
// Eval PHP code to get default value. Note that access of
// undefined class constants can cause the eval() to
// fatal. Zend lets such fatals propagate, so don't bother catching
// exceptions here.
CVarRef v = g_vmContext->getEvaledArg(
fpi.phpCode(),
func->cls() ? func->cls()->nameRef() : func->nameRef()
);
param.set(s_default, v);
}
param.set(s_defaultText, VarNR(fpi.phpCode()));
}
if (func->byRef(i)) {
param.set(s_ref, true_varNR);
}
{
Array userAttrs = Array::Create();
for (auto it = fpi.userAttributes().begin();
it != fpi.userAttributes().end(); ++it) {
userAttrs.set(String(const_cast<StringData*>(it->first)),
tvAsCVarRef(&it->second));
}
param.set(s_attributes, VarNR(userAttrs));
}
arr.append(VarNR(param));
}
ret.set(s_params, VarNR(arr));
}
// static variables
{
Array arr = Array::Create();
const Func::SVInfoVec& staticVars = func->staticVars();
for (unsigned int i = 0; i < staticVars.size(); i++) {
const Func::SVInfo &sv = staticVars[i];
arr.set(VarNR(sv.name), VarNR(sv.phpCode));
}
ret.set(s_static_variables, VarNR(arr));
}
// user attributes
{
Array arr = Array::Create();
for (auto it = func->userAttributes().begin();
it != func->userAttributes().end(); ++it) {
arr.set(String(const_cast<StringData*>(it->first)),
tvAsCVarRef(&it->second));
}
ret.set(s_attributes, VarNR(arr));
}
// closure info
ret.set(s_is_closure, func->isClosureBody());
// Interestingly this isn't the same as calling isGenerator() because calling
// isGenerator() on the outside function for a generator returns false.
ret.set(s_is_generator, func->hasGeneratorAsBody());
}
static void set_method_info(Array &ret, ClassInfo::MethodInfo *info,
const ClassInfo *cls) {
ret.set(s_name, info->name);
set_attrs(ret, get_modifiers(info->attribute, false));
if (info->attribute & ClassInfo::IsConstructor) {
ret.set(s_constructor, true_varNR);
}
ret.set(s_class, VarNR(cls->getName()));
set_function_info(ret, info, &cls->getName());
set_source_info(ret, info->file, info->line1, info->line2);
}
static bool isConstructor(const Func* func) {
PreClass* pcls = func->preClass();
if (!pcls) return false;
if (func->cls()) return func == func->cls()->getCtor();
/* A same named function is not a constructor in a trait,
or if the function was imported from a trait */
if ((pcls->attrs() | func->attrs()) & AttrTrait) return false;
if (!strcasecmp("__construct", func->name()->data())) return true;
return pcls->name()->isame(func->name());
}
static void set_method_info(Array &ret, const Func* func) {
ret.set(s_name, VarNR(func->nameRef()));
set_attrs(ret, get_modifiers(func->attrs(), false));
if (isConstructor(func)) {
ret.set(s_constructor, true_varNR);
}
ret.set(s_class, VarNR(func->cls() ? func->cls()->name() :
func->preClass()->name()));
set_function_info(ret, func);
set_source_info(ret, func->unit()->filepath()->data(),
func->line1(), func->line2());
}
static Array get_method_info(const ClassInfo *cls, CVarRef name) {
if (!cls) return Array();
ClassInfo *owner;
ClassInfo::MethodInfo *meth = cls->hasMethod(
name.toString(), owner,
cls->getAttribute() & (ClassInfo::IsInterface|ClassInfo::IsAbstract));
if (!meth) return Array();
Array ret;
set_method_info(ret, meth, owner);
return ret;
}
Array f_hphp_get_method_info(CVarRef cls, CVarRef name) {
const Class* c = get_cls(cls);
if (!c) return Array();
if (c->clsInfo()) {
/*
* Default arguments for builtins arent setup correctly,
* so use the ClassInfo instead.
*/
return get_method_info(c->clsInfo(), name);
}
const String& method_name = name.toString();
const Func* func = c->lookupMethod(method_name.get());
if (!func) {
if (c->attrs() & AttrAbstract) {
const Class::InterfaceMap& ifaces = c->allInterfaces();
for (int i = 0, size = ifaces.size(); i < size; i++) {
func = ifaces[i]->lookupMethod(method_name.get());
if (func) break;
}
}
if (!func) return Array();
}
Array ret;
set_method_info(ret, func);
return ret;
}
Array f_hphp_get_closure_info(CVarRef closure) {
Array mi = f_hphp_get_method_info(closure.toObject()->o_getClassName(),
s___invoke);
mi.set(s_name, s_closure_in_braces);
mi.set(s_closureobj, closure);
mi.set(s_closure, empty_string);
mi.remove(s_access);
mi.remove(s_accessible);
mi.remove(s_modifiers);
mi.remove(s_class);
Array &params = mi.lvalAt(s_params).asArrRef();
for (int i = 0; i < params.size(); i++) {
params.lvalAt(i).asArrRef().remove(s_class);
}
return mi;
}
Variant f_hphp_get_class_constant(CVarRef cls, CVarRef name) {
return cellAsCVarRef(
g_vmContext->lookupClsCns(cls.toString().get(),
name.toString().get())
);
}
static Array get_class_info(const ClassInfo *cls) {
Array ret;
const String& className = cls->getName();
ret.set(s_name, className);
ret.set(s_extension, empty_string);
ret.set(s_parent, cls->getParentClass());
// interfaces
{
Array arr = Array::Create();
const ClassInfo::InterfaceVec &interfaces = cls->getInterfacesVec();
for (ClassInfo::InterfaceVec::const_iterator iter = interfaces.begin();
iter != interfaces.end(); ++iter) {
arr.set(*iter, 1);
}
ret.set(s_interfaces, VarNR(arr));
}
// traits
{
Array arr = Array::Create();
const ClassInfo::TraitVec &traits = cls->getTraitsVec();
for (ClassInfo::TraitVec::const_iterator iter = traits.begin();
iter != traits.end(); ++iter) {
arr.set(*iter, 1);
}
ret.set(s_traits, VarNR(arr));
}
// trait aliases
{
Array arr = Array::Create();
const ClassInfo::TraitAliasVec &aliases = cls->getTraitAliasesVec();
for (ClassInfo::TraitAliasVec::const_iterator iter = aliases.begin();
iter != aliases.end(); ++iter) {
arr.set(iter->first, iter->second);
}
ret.set(s_trait_aliases, VarNR(arr));
}
// attributes
{
int attribute = cls->getAttribute();
if (attribute & ClassInfo::IsSystem) {
ret.set(s_internal, true_varNR);
}
if (attribute & ClassInfo::HipHopSpecific) {
ret.set(s_hphp, true_varNR);
}
if (attribute & ClassInfo::IsFinal) {
ret.set(s_final, true_varNR);
}
if (attribute & ClassInfo::IsAbstract) {
ret.set(s_abstract, true_varNR);
}
if (attribute & ClassInfo::IsInterface) {
ret.set(s_interface, true_varNR);
}
if (attribute & ClassInfo::IsTrait) {
ret.set(s_trait, true_varNR);
}
ret.set(s_modifiers, VarNR(get_modifiers(attribute, true)));
}
// methods
{
Array arr = Array::Create();
const ClassInfo::MethodVec &methods = cls->getMethodsVec();
for (ClassInfo::MethodVec::const_iterator iter = methods.begin();
iter != methods.end(); ++iter) {
ClassInfo::MethodInfo *m = *iter;
Array info = Array::Create();
set_method_info(info, m, cls);
arr.set(f_strtolower(m->name), info);
}
ret.set(s_methods, VarNR(arr));
}
// properties
{
Array arr = Array::Create();
Array arrPriv = Array::Create();
const ClassInfo::PropertyVec &properties = cls->getPropertiesVec();
for (ClassInfo::PropertyVec::const_iterator iter = properties.begin();
iter != properties.end(); ++iter) {
ClassInfo::PropertyInfo *prop = *iter;
Array info = Array::Create();
set_property_info(info, prop, cls);
if (prop->attribute & ClassInfo::IsPrivate) {
assert(prop->owner == cls);
arrPriv.set(prop->name, info);
} else {
arr.set(prop->name, info);
}
}
ret.set(s_properties, VarNR(arr));
ret.set(s_private_properties, VarNR(arrPriv));
}
// constants
{
Array arr = Array::Create();
const ClassInfo::ConstantVec &constants = cls->getConstantsVec();
for (ClassInfo::ConstantVec::const_iterator iter = constants.begin();
iter != constants.end(); ++iter) {
ClassInfo::ConstantInfo *info = *iter;
arr.set(info->name, info->getValue());
}
ret.set(s_constants, VarNR(arr));
}
{ // source info
set_source_info(ret, cls->getFile(), cls->getLine1(),
cls->getLine2());
set_doc_comment(ret, cls->getDocComment());
}
// user attributes
{
Array arr = Array::Create();
const ClassInfo::UserAttributeVec &userAttrs = cls->getUserAttributeVec();
for (ClassInfo::UserAttributeVec::const_iterator iter = userAttrs.begin();
iter != userAttrs.end(); ++iter) {
ClassInfo::UserAttributeInfo *info = *iter;
arr.set(info->name, info->getValue());
}
ret.set(s_attributes, VarNR(arr));
}
return ret;
}
Array f_hphp_get_class_info(CVarRef name) {
const Class* cls = get_cls(name);
if (!cls) return Array();
if (cls->clsInfo()) {
/*
* Default arguments for builtins arent setup correctly,
* so use the ClassInfo instead.
*/
return get_class_info(cls->clsInfo());
}
Array ret;
ret.set(s_name, VarNR(cls->name()));
ret.set(s_extension, empty_string);
ret.set(s_parent, cls->parentRef());
// interfaces
{
Array arr = Array::Create();
for (auto const& interface : cls->declInterfaces()) {
arr.set(interface->nameRef(), VarNR(1));
}
ret.set(s_interfaces, VarNR(arr));
}
// traits
{
Array arr = Array::Create();
for (auto const& trait : cls->usedTraits()) {
arr.set(trait->nameRef(), VarNR(1));
}
ret.set(s_traits, VarNR(arr));
}
// trait aliases
{
Array arr = Array::Create();
const Class::TraitAliasVec& aliases = cls->traitAliases();
for (int i = 0, s = aliases.size(); i < s; ++i) {
arr.set(*(String*)&aliases[i].first, VarNR(aliases[i].second));
}
ret.set(s_trait_aliases, VarNR(arr));
}
// attributes
{
if (false) {
ret.set(s_internal, true_varNR);
}
if (false && ClassInfo::HipHopSpecific) {
ret.set(s_hphp, true_varNR);
}
if (cls->attrs() & AttrFinal) {
ret.set(s_final, true_varNR);
}
if (cls->attrs() & AttrAbstract) {
ret.set(s_abstract, true_varNR);
}
if (cls->attrs() & AttrInterface) {
ret.set(s_interface, true_varNR);
}
if (cls->attrs() & AttrTrait) {
ret.set(s_trait, true_varNR);
}
ret.set(s_modifiers, VarNR(get_modifiers(cls->attrs(), true)));
}
// methods
{
Array arr = Array::Create();
Func* const* methods = cls->preClass()->methods();
size_t const numMethods = cls->preClass()->numMethods();
for (Slot i = 0; i < numMethods; ++i) {
const Func* m = methods[i];
if (m->isGenerated()) continue;
Array info = Array::Create();
set_method_info(info, m);
arr.set(f_strtolower(m->nameRef()), VarNR(info));
}
Func* const* clsMethods = cls->methods();
for (Slot i = cls->traitsBeginIdx();
i < cls->traitsEndIdx();
++i) {
const Func* m = clsMethods[i];
if (m->isGenerated()) continue;
Array info = Array::Create();
set_method_info(info, m);
arr.set(f_strtolower(m->nameRef()), VarNR(info));
}
ret.set(s_methods, VarNR(arr));
}
// properties
{
Array arr = Array::Create();
Array arrPriv = Array::Create();
const Class::Prop* properties = cls->declProperties();
const size_t nProps = cls->numDeclProperties();
for (Slot i = 0; i < nProps; ++i) {
const Class::Prop& prop = properties[i];
Array info = Array::Create();
if ((prop.m_attrs & AttrPrivate) == AttrPrivate) {
if (prop.m_class == cls) {
set_instance_prop_info(info, &prop);
arrPriv.set(*(String*)(&prop.m_name), VarNR(info));
}
continue;
}
set_instance_prop_info(info, &prop);
arr.set(*(String*)(&prop.m_name), VarNR(info));
}
const Class::SProp* staticProperties = cls->staticProperties();
const size_t nSProps = cls->numStaticProperties();
for (Slot i = 0; i < nSProps; ++i) {
const Class::SProp& prop = staticProperties[i];
Array info = Array::Create();
if ((prop.m_attrs & AttrPrivate) == AttrPrivate) {
if (prop.m_class == cls) {
set_static_prop_info(info, &prop);
arrPriv.set(*(String*)(&prop.m_name), VarNR(info));
}
continue;
}
set_static_prop_info(info, &prop);
arr.set(*(String*)(&prop.m_name), VarNR(info));
}
ret.set(s_properties, VarNR(arr));
ret.set(s_private_properties, VarNR(arrPriv));
}
// constants
{
Array arr = Array::Create();
size_t numConsts = cls->numConstants();
const Class::Const* consts = cls->constants();
for (size_t i = 0; i < numConsts; i++) {
// Note: hphpc doesn't include inherited constants in
// get_class_constants(), so mimic that behavior
if (consts[i].m_class == cls) {
Cell value = cls->clsCnsGet(consts[i].m_name);
assert(value.m_type != KindOfUninit);
arr.set(consts[i].nameRef(), cellAsCVarRef(value));
}
}
ret.set(s_constants, VarNR(arr));
}
{ // source info
const PreClass* pcls = cls->preClass();
set_source_info(ret, pcls->unit()->filepath()->data(),
pcls->line1(), pcls->line2());
set_doc_comment(ret, pcls->docComment());
}
// user attributes
{
Array arr = Array::Create();
const PreClass* pcls = cls->preClass();
for (auto it = pcls->userAttributes().begin();
it != pcls->userAttributes().end(); ++it) {
arr.set(String(const_cast<StringData*>(it->first)),
tvAsCVarRef(&it->second));
}
ret.set(s_attributes, VarNR(arr));
}
return ret;
}
Array f_hphp_get_function_info(const String& name) {
Array ret;
const Func* func = Unit::loadFunc(name.get());
if (!func) return ret;
ret.set(s_name, VarNR(func->name()));
ret.set(s_closure, empty_string);
// setting parameters and static variables
set_function_info(ret, func);
set_source_info(ret, func->unit()->filepath()->data(),
func->line1(), func->line2());
return ret;
}
Variant f_hphp_invoke(const String& name, CArrRef params) {
return invoke(name.data(), params);
}
Variant f_hphp_invoke_method(CVarRef obj, const String& cls, const String& name,
CArrRef params) {
if (!obj.isObject()) {
return invoke_static_method(cls, name, params);
}
ObjectData *o = obj.toCObjRef().get();
return o->o_invoke(name, params);
}
bool f_hphp_instanceof(CObjRef obj, const String& name) {
return obj.instanceof(name.data());
}
Object f_hphp_create_object(const String& name, CArrRef params) {
return g_vmContext->createObject(name.get(), params);
}
Object f_hphp_create_object_without_constructor(const String& name) {
return g_vmContext->createObject(name.get(), nullptr, false);
}
Variant f_hphp_get_property(CObjRef obj, const String& cls, const String& prop) {
return obj->o_get(prop, true /* error */, cls);
}
void f_hphp_set_property(CObjRef obj, const String& cls, const String& prop,
CVarRef value) {
if (!cls.empty() && RuntimeOption::RepoAuthoritative) {
raise_error(
"We've already made many assumptions about private variables. "
"You can't change accessibility in Whole Program mode"
);
}
obj->o_set(prop, value, cls);
}
Variant f_hphp_get_static_property(const String& cls, const String& prop, bool force) {
StringData* sd = cls.get();
Class* class_ = lookup_class(sd);
if (!class_) {
raise_error("Non-existent class %s", sd->data());
}
VMRegAnchor _;
bool visible, accessible;
TypedValue* tv = class_->getSProp(
force ? class_ : arGetContextClass(g_vmContext->getFP()),
prop.get(), visible, accessible
);
if (tv == nullptr) {
raise_error("Class %s does not have a property named %s",
sd->data(), prop.get()->data());
}
if (!visible || !accessible) {
raise_error("Invalid access to class %s's property %s",
sd->data(), prop.get()->data());
}
return tvAsVariant(tv);
}
void f_hphp_set_static_property(const String& cls, const String& prop, CVarRef value,
bool force) {
StringData* sd = cls.get();
Class* class_ = lookup_class(sd);
if (!class_) {
raise_error("Non-existent class %s", sd->data());
}
VMRegAnchor _;
bool visible, accessible;
TypedValue* tv = class_->getSProp(
force ? class_ : arGetContextClass(g_vmContext->getFP()),
prop.get(), visible, accessible
);
if (tv == nullptr) {
raise_error("Class %s does not have a property named %s",
cls.get()->data(), prop.get()->data());
}
if (!visible || !accessible) {
raise_error("Invalid access to class %s's property %s",
sd->data(), prop.get()->data());
}
tvAsVariant(tv) = value;
}
String f_hphp_get_original_class_name(const String& name) {
Class* cls = Unit::loadClass(name.get());
if (!cls) return empty_string;
return cls->nameRef();
}
bool f_hphp_scalar_typehints_enabled() {
return RuntimeOption::EnableHipHopSyntax;
}
///////////////////////////////////////////////////////////////////////////////
}