Update the caching for a few ReflectionClass functions to be sane
[hiphop-php.git] / hphp / runtime / ext / reflection / ext_reflection.cpp
blob8b654335ffb474c4b452561c584715db70929e11
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/reflection/ext_reflection.h"
20 #include "hphp/runtime/base/array-init.h"
21 #include "hphp/runtime/base/externals.h"
22 #include "hphp/runtime/base/file.h"
23 #include "hphp/runtime/base/mixed-array.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/base/string-hash-set.h"
26 #include "hphp/runtime/base/string-util.h"
27 #include "hphp/runtime/base/tv-refcount.h"
28 #include "hphp/runtime/base/type-structure.h"
29 #include "hphp/runtime/base/type-variant.h"
30 #include "hphp/runtime/base/unit-cache.h"
31 #include "hphp/runtime/vm/jit/translator-inline.h"
32 #include "hphp/runtime/vm/native-data.h"
33 #include "hphp/runtime/vm/native-prop-handler.h"
35 #include "hphp/runtime/ext/debugger/ext_debugger.h"
36 #include "hphp/runtime/ext/std/ext_std_closure.h"
37 #include "hphp/runtime/ext/collections/ext_collections-set.h"
38 #include "hphp/runtime/ext/std/ext_std_misc.h"
39 #include "hphp/runtime/ext/string/ext_string.h"
40 #include "hphp/runtime/ext/extension-registry.h"
42 #include "hphp/system/systemlib.h"
44 #include <functional>
45 #include <boost/algorithm/string/predicate.hpp>
47 namespace HPHP {
49 ///////////////////////////////////////////////////////////////////////////////
51 const StaticString
52 s_name("name"),
53 s___name("__name"),
54 s_version("version"),
55 s_info("info"),
56 s_ini("ini"),
57 s_constants("constants"),
58 s_constructor("constructor"),
59 s_functions("functions"),
60 s_classes("classes"),
61 s_access("access"),
62 s_public("public"),
63 s_protected("protected"),
64 s_private("private"),
65 s_file("file"),
66 s_line1("line1"),
67 s_line2("line2"),
68 s_doc("doc"),
69 s_modifiers("modifiers"),
70 s_class("class"),
71 s_prototype("prototype"),
72 s_ref("ref"),
73 s_inout("inout"),
74 s_index("index"),
75 s_type("type"),
76 s_nullable("nullable"),
77 s_msg("msg"),
78 s_is_optional("is_optional"),
79 s_is_variadic("is_variadic"),
80 s_default("default"),
81 s_defaultValue("defaultValue"),
82 s_defaultText("defaultText"),
83 s_params("params"),
84 s_final("final"),
85 s_abstract("abstract"),
86 s_instantiable("instantiable"),
87 s_internal("internal"),
88 s_is_async("is_async"),
89 s_is_closure("is_closure"),
90 s_is_generator("is_generator"),
91 s_hphp("hphp"),
92 s_static_variables("static_variables"),
93 s_extension("extension"),
94 s_interfaces("interfaces"),
95 s_traits("traits"),
96 s_interface("interface"),
97 s_trait("trait"),
98 s_methods("methods"),
99 s_properties("properties"),
100 s_private_properties("private_properties"),
101 s_properties_index("properties_index"),
102 s_private_properties_index("private_properties_index"),
103 s_attributes("attributes"),
104 s_function("function"),
105 s_trait_aliases("trait_aliases"),
106 s_varg("varg"),
107 s___invoke("__invoke"),
108 s_return_type("return_type"),
109 s_accessible("accessible"),
110 s_reflectionexception("ReflectionException"),
111 s_reflectionextension("ReflectionExtension"),
112 s_type_hint("type_hint"),
113 s_type_hint_builtin("type_hint_builtin"),
114 s_type_hint_nullable("type_hint_nullable");
116 Class* Reflection::s_ReflectionExceptionClass = nullptr;
117 Class* Reflection::s_ReflectionExtensionClass = nullptr;
119 Class* get_cls(const Variant& class_or_object) {
120 if (class_or_object.is(KindOfObject)) {
121 return class_or_object.toCObjRef()->getVMClass();
122 } else if (class_or_object.isClass()) {
123 return class_or_object.toClassVal();
126 return Unit::loadClass(class_or_object.toString().get());
129 const Func* get_method_func(const Class* cls, const String& meth_name) {
130 const Func* func = cls->lookupMethod(meth_name.get());
131 if (!func) {
132 if (cls->attrs() & (AttrInterface | AttrAbstract | AttrTrait)) {
133 const Class::InterfaceMap& ifaces = cls->allInterfaces();
134 for (int i = 0, size = ifaces.size(); i < size; i++) {
135 func = ifaces[i]->lookupMethod(meth_name.get());
136 if (func) { break; }
140 assertx(func == nullptr || func->isMethod());
141 return func;
144 Variant default_arg_from_php_code(const Func::ParamInfo& fpi,
145 const Func* func, unsigned argIdx) {
146 assertx(fpi.hasDefaultValue());
147 if (fpi.hasScalarDefaultValue()) {
148 // Most of the time the default value is scalar, so we can
149 // avoid evaling in the common case
150 return tvAsVariant((TypedValue*)&fpi.defaultValue);
152 // Eval PHP code to get the default value. Note that eval here can throw a
153 // fatal, e.g. due to the use of undefined class constants or static:: .
154 // When this happens, instead of fataling the execution, set the default
155 // value to null and raise a warning.
156 try {
157 return g_context->getEvaledArg(
158 fpi.phpCode,
159 // We use cls() instead of implCls() because we want the namespace and
160 // class context for which the closure is scoped, not that of the
161 // Closure subclass (which, among other things, is always globally
162 // namespaced).
163 func->cls() ? func->cls()->nameStr() : func->nameStr(),
164 func->unit()
166 } catch (Exception& e) {
167 raise_warning("Failed to eval default value of %s()'s $%s argument for "
168 "reflection, assigning NULL instead",
169 func->fullNameStr().data(),
170 func->localNames()[argIdx]->data());
171 return init_null_variant;
175 Array HHVM_FUNCTION(hphp_get_extension_info, const String& name) {
176 Array ret;
178 Extension *ext = ExtensionRegistry::get(name);
180 ret.set(s_name, name);
181 ret.set(s_version, ext ? ext->getVersion() : "");
182 ret.set(s_info, empty_string_variant_ref);
183 ret.set(s_ini, Array::Create());
184 ret.set(s_constants, Array::Create());
185 ret.set(s_functions, Array::Create());
186 ret.set(s_classes, Array::Create());
188 return ret;
191 int get_modifiers(Attr attrs, bool cls, bool prop) {
192 int php_modifier = 0;
193 if (!prop) {
194 // These bits have different meanings with properties
195 if (attrs & AttrAbstract) php_modifier |= cls ? 0x20 : 0x02;
196 if (attrs & AttrFinal) php_modifier |= cls ? 0x40 : 0x04;
198 if (!cls) { // AttrPublic bits are not valid on class (have other meaning)
199 if (attrs & AttrStatic) php_modifier |= 0x01;
200 if (attrs & AttrPublic) php_modifier |= 0x100;
201 if (attrs & AttrProtected) php_modifier |= 0x200;
202 if (attrs & AttrPrivate) php_modifier |= 0x400;
204 return php_modifier;
207 ALWAYS_INLINE
208 static Attr attrs_from_modifiers(int php_modifier, bool cls) {
209 Attr attrs = (Attr) 0;
210 if (php_modifier & (cls ? 0x20 : 0x02)) {
211 attrs = (Attr)(attrs | AttrAbstract);
213 if (php_modifier & (cls ? 0x40 : 0x04)) {
214 attrs = (Attr)(attrs | AttrFinal);
216 if (php_modifier & 0x01) { attrs = (Attr)(attrs | AttrStatic); }
217 if (!cls) { // AttrPublic bits are not valid on class (have other meaning)
218 if (php_modifier & 0x100) { attrs = (Attr)(attrs | AttrPublic); }
219 if (php_modifier & 0x200) { attrs = (Attr)(attrs | AttrProtected); }
220 if (php_modifier & 0x400) { attrs = (Attr)(attrs | AttrPrivate); }
222 return attrs;
225 static void set_attrs(Array& ret, int modifiers) {
226 if (modifiers & 0x100) {
227 ret.set(s_access, VarNR(s_public).tv());
228 ret.set(s_accessible, true_varNR.tv());
229 } else if (modifiers & 0x200) {
230 ret.set(s_access, VarNR(s_protected).tv());
231 ret.set(s_accessible, false_varNR.tv());
232 } else if (modifiers & 0x400) {
233 ret.set(s_access, VarNR(s_private).tv());
234 ret.set(s_accessible, false_varNR.tv());
235 } else {
236 assertx(false);
238 ret.set(s_modifiers, make_tv<KindOfInt64>(modifiers));
239 if (modifiers & 0x1) {
240 ret.set(s_static, true_varNR.tv());
242 if (modifiers & 0x44) {
243 ret.set(s_final, true_varNR.tv());
245 if (modifiers & 0x22) {
246 ret.set(s_abstract, true_varNR.tv());
250 static void set_empty_doc_comment(Array& ret) {
251 ret.set(s_doc, false_varNR.tv());
254 static void set_doc_comment(Array& ret,
255 const StringData* comment,
256 bool isBuiltin) {
257 if (comment == nullptr || comment->empty()) {
258 set_empty_doc_comment(ret);
259 } else if (isBuiltin && !HHVM_FUNCTION(hphp_debugger_attached)) {
260 set_empty_doc_comment(ret);
261 } else {
262 assertx(!comment->isRefCounted());
263 ret.set(s_doc, make_tv<KindOfPersistentString>(comment));
267 static void set_instance_prop_info(Array& ret,
268 const Class::Prop* prop,
269 const Variant& default_val) {
270 ret.set(s_name, make_tv<KindOfPersistentString>(prop->name));
271 ret.set(s_default, true_varNR.tv());
272 ret.set(s_defaultValue, default_val);
273 set_attrs(ret, get_modifiers(prop->attrs, false, true) & ~0x66);
274 ret.set(s_class, make_tv<KindOfPersistentString>(prop->cls->name()));
275 set_doc_comment(ret, prop->preProp->docComment(), prop->cls->isBuiltin());
277 auto const user_type = prop->preProp->userType();
278 if (user_type && user_type->size()) {
279 ret.set(s_type, make_tv<KindOfPersistentString>(user_type));
280 } else {
281 ret.set(s_type, false_varNR.tv());
285 static void set_dyn_prop_info(
286 Array &ret,
287 Cell name,
288 const StringData* className) {
289 ret.set(s_name, name);
290 set_attrs(ret, get_modifiers(AttrPublic, false, true) & ~0x66);
291 ret.set(s_class, make_tv<KindOfPersistentString>(className));
292 set_empty_doc_comment(ret);
293 ret.set(s_type, false_varNR.tv());
296 static void set_static_prop_info(Array &ret, const Class::SProp* prop) {
297 ret.set(s_name, make_tv<KindOfPersistentString>(prop->name));
298 ret.set(s_default, true_varNR.tv());
299 ret.set(s_defaultValue, prop->val);
300 set_attrs(ret, get_modifiers(prop->attrs, false, true) & ~0x66);
301 ret.set(s_class, make_tv<KindOfPersistentString>(prop->cls->name()));
302 set_doc_comment(ret, prop->preProp->docComment(), prop->cls->isBuiltin());
303 auto const user_type = prop->preProp->userType();
304 if (user_type && user_type->size()) {
305 ret.set(s_type, make_tv<KindOfPersistentString>(user_type));
306 } else {
307 ret.set(s_type, false_varNR.tv());
311 static bool resolveConstant(const char *p, int64_t len, Variant &cns) {
312 // ltrim
313 while (len && (*p == ' ')) {
314 p++;
315 len--;
317 // rtrim
318 while (len && (p[len-1] == ' ')) {
319 len--;
322 String cname(p, len, CopyString);
324 if (!f_defined(cname)) {
325 cns = uninit_null();
326 return false;
329 cns = f_constant(cname);
330 return true;
333 bool resolveDefaultParameterConstant(const char *value, int64_t valueLen,
334 Variant &cns) {
335 const char *p = value;
336 const char *e = value + valueLen;
337 const char *s;
338 bool isLval = false;
339 int64_t lval = 0;
341 while ((s = strchr(p, '|'))) {
342 isLval = true;
343 if (!resolveConstant(p, s - p, cns)) {
344 return false;
346 lval |= cns.toInt64();
347 p = s + 1;
349 if (!resolveConstant(p, e - p, cns)) {
350 return false;
352 if (isLval) {
353 cns = cns.toInt64() | lval;
355 return true;
358 static bool isConstructor(const Func* func) {
359 PreClass* pcls = func->preClass();
360 if (!pcls || (pcls->attrs() & AttrInterface)) { return false; }
361 if (func->implCls()) { return func == func->implCls()->getCtor(); }
362 if (0 == strcasecmp("__construct", func->name()->data())) { return true; }
363 /* A same named function is not a constructor in a trait */
364 if (pcls->attrs() & AttrTrait) return false;
365 return pcls->name()->isame(func->name());
368 static const Class* get_prototype_class_from_interfaces(const Class *cls,
369 const Func *func) {
370 // only looks at the interfaces if the method is public
371 if (!func->isPublic()) return nullptr;
372 const Class::InterfaceMap& interfaces = cls->allInterfaces();
373 for (unsigned int i = 0, size = interfaces.size(); i < size; i++) {
374 const Class* iface = interfaces[i];
375 if (iface->preClass()->hasMethod(func->name())) return iface;
377 return nullptr;
380 Variant HHVM_FUNCTION(hphp_invoke, const String& name, const Variant& params) {
381 return invoke(name.data(), params);
384 static const StaticString s_invoke_not_instanceof_error(
385 "Given object is not an instance of the class this method was declared in"
388 static const StaticString s_invoke_non_object(
389 "Non-object passed to Invoke()"
392 Variant HHVM_FUNCTION(hphp_invoke_method, const Variant& obj,
393 const String& cls,
394 const String& name,
395 const Variant& params) {
396 if (obj.isNull()) {
397 return invoke_static_method(cls, name, params);
400 if (!obj.is(KindOfObject)) {
401 Reflection::ThrowReflectionExceptionObject(s_invoke_non_object);
404 auto const providedClass = Unit::loadClass(cls.get());
405 if (!providedClass) {
406 raise_error("Call to undefined method %s::%s()", cls.data(), name.data());
408 auto const selectedFunc = providedClass->lookupMethod(name.get());
409 if (!selectedFunc) {
410 raise_error("Call to undefined method %s::%s()", cls.data(), name.data());
413 auto const objData = obj.toCObjRef().get();
414 auto const implementingClass = selectedFunc->implCls();
415 if (!objData->instanceof(implementingClass)) {
416 Reflection::ThrowReflectionExceptionObject(s_invoke_not_instanceof_error);
419 // Get the CallCtx this way instead of using vm_decode_function() because
420 // vm_decode_function() has no way to specify a class independent from the
421 // class::function being called.
422 // Note that this breaks the rules for name lookup (for protected and private)
423 // but that's okay because so does Zend's implementation.
424 CallCtx ctx;
425 ctx.cls = providedClass;
426 ctx.this_ = objData;
427 ctx.invName = nullptr;
428 ctx.func = selectedFunc;
429 ctx.dynamic = true;
431 return Variant::attach(
432 g_context->invokeFunc(ctx, params)
436 Object HHVM_FUNCTION(hphp_create_object, const String& name,
437 const Variant& params) {
438 return Object::attach(g_context->createObject(name.get(), params));
441 Object HHVM_FUNCTION(hphp_create_object_without_constructor,
442 const String& name) {
443 return Object::attach(
444 g_context->createObject(name.get(), init_null_variant, false)
448 Variant HHVM_FUNCTION(hphp_get_property, const Object& obj, const String& cls,
449 const String& prop) {
450 /* It's possible to get a ReflectionProperty for a property which
451 * no longer exists. Silentyly fail to match PHP5 behavior
453 return obj->o_get(prop, false /* error */, cls);
456 void HHVM_FUNCTION(hphp_set_property, const Object& obj, const String& cls,
457 const String& prop, const Variant& value) {
458 if (!cls.empty() && RuntimeOption::EvalAuthoritativeMode) {
459 raise_error(
460 "We've already made many assumptions about private variables. "
461 "You can't change accessibility in Whole Program mode"
465 obj->o_set(prop, value, cls);
468 Variant HHVM_FUNCTION(hphp_get_static_property, const String& cls,
469 const String& prop,
470 bool force) {
471 auto const sd = cls.get();
472 auto const class_ = Unit::lookupClass(sd);
473 if (!class_) {
474 raise_error("Non-existent class %s", sd->data());
476 VMRegAnchor _;
478 auto const lookup = class_->getSPropIgnoreLateInit(
479 force ? class_ : arGetContextClass(vmfp()),
480 prop.get()
482 if (!lookup.val) {
483 raise_error("Class %s does not have a property named %s",
484 sd->data(), prop.get()->data());
486 if (!lookup.accessible) {
487 raise_error("Invalid access to class %s's property %s",
488 sd->data(), prop.get()->data());
490 return tvAsVariant(lookup.val);
493 void HHVM_FUNCTION(hphp_set_static_property, const String& cls,
494 const String& prop,
495 const Variant& value,
496 bool force) {
497 if (RuntimeOption::EvalAuthoritativeMode) {
498 raise_error("Setting static properties through reflection is not "
499 "allowed in RepoAuthoritative mode");
501 auto const sd = cls.get();
502 auto const class_ = Unit::lookupClass(sd);
504 if (!class_) raise_error("Non-existent class %s", sd->data());
506 VMRegAnchor _;
508 auto const lookup = class_->getSPropIgnoreLateInit(
509 force ? class_ : arGetContextClass(vmfp()),
510 prop.get()
512 if (!lookup.val) {
513 raise_error("Class %s does not have a property named %s",
514 cls.get()->data(), prop.get()->data());
516 if (!lookup.accessible) {
517 raise_error("Invalid access to class %s's property %s",
518 sd->data(), prop.get()->data());
521 auto const& sprop = class_->staticProperties()[lookup.slot];
522 auto const& tc = sprop.typeConstraint;
523 auto const temp = value.asInitCellTmp();
524 if (RuntimeOption::EvalCheckPropTypeHints > 0 && tc.isCheckable()) {
525 tc.verifyStaticProperty(&temp, class_, sprop.cls, prop.get());
527 tvAsVariant(lookup.val) = value;
531 * cls_or_obj: the name of a class or an instance of the class;
532 * cns_name: the name of the type constant of the class;
534 * If the type constant exists and is not abstract, this function
535 * returns the shape representing the type associated with the type
536 * constant.
538 Array HHVM_FUNCTION(type_structure,
539 const Variant& cls_or_obj, const Variant& cns_name) {
540 auto const cns_sd = cns_name.getStringDataOrNull();
541 if (!cns_sd) {
542 auto name = cls_or_obj.toString();
544 auto const typeAlias = Unit::loadTypeAlias(name.get());
546 if (!typeAlias) {
547 raise_error("Non-existent type alias %s", name.get()->data());
550 auto const typeStructure = typeAlias->typeStructure;
551 assertx(!typeStructure.empty());
552 assertx(typeStructure.isDictOrDArray());
553 Array resolved;
554 try {
555 bool persistent = true;
556 resolved = TypeStructure::resolve(name, typeStructure, persistent);
557 } catch (Exception& e) {
558 raise_error("resolving type alias %s failed. "
559 "Have you declared all classes in the type alias",
560 name.get()->data());
562 assertx(!resolved.empty());
563 assertx(resolved.isDictOrDArray());
564 return resolved;
567 auto const cls = get_cls(cls_or_obj);
569 if (!cls) {
570 raise_error("Class undefined: %s", cls_or_obj.toString().get()->data());
573 auto const cls_sd = cls->name();
574 auto typeCns = cls->clsCnsGet(cns_sd, true);
575 if (typeCns.m_type == KindOfUninit) {
576 if (cls->hasTypeConstant(cns_sd, true)) {
577 raise_error("Type constant %s::%s is abstract",
578 cls_sd->data(), cns_sd->data());
579 } else {
580 raise_error("Non-existent type constant %s::%s",
581 cls_sd->data(), cns_sd->data());
585 assertx(isArrayLikeType(typeCns.m_type));
586 assertx(typeCns.m_data.parr->isDictOrDArray());
587 assertx(typeCns.m_data.parr->isStatic());
588 return Array::attach(typeCns.m_data.parr);
591 String HHVM_FUNCTION(hphp_get_original_class_name, const String& name) {
592 Class* cls = Unit::loadClass(name.get());
593 if (!cls) return empty_string();
594 return cls->nameStr();
597 [[noreturn]]
598 void Reflection::ThrowReflectionExceptionObject(const Variant& message) {
599 Object inst{s_ReflectionExceptionClass};
600 tvDecRefGen(
601 g_context->invokeFunc(s_ReflectionExceptionClass->getCtor(),
602 make_vec_array(message),
603 inst.get())
605 throw_object(inst);
609 /////////////////////////////////////////////////////////////////////////////
610 // class ReflectionFuncHandle
612 const StaticString s_ReflectionFuncHandle("ReflectionFuncHandle");
614 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getFileName) {
615 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
616 if (func->isBuiltin()) {
617 return false;
619 auto file = func->unit()->filepath()->data();
620 if (!file) { file = ""; }
621 if (file[0] != '/') {
622 return String(RuntimeOption::SourceRoot + file);
623 } else {
624 return String(file);
628 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getStartLine) {
629 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
630 if (func->isBuiltin()) {
631 return false;
633 return func->line1();
636 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getEndLine) {
637 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
638 if (func->isBuiltin()) {
639 return false;
641 return func->line2();
644 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getDocComment) {
645 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
646 auto const comment = func->docComment();
647 if (comment == nullptr || comment->empty()) {
648 return false_varNR;
649 } else if (func->isBuiltin() && !HHVM_FUNCTION(hphp_debugger_attached)) {
650 return false_varNR;
651 } else {
652 auto ret = const_cast<StringData*>(comment);
653 return VarNR(ret);
657 ALWAYS_INLINE
658 static Array get_function_static_variables(const Func* func) {
659 auto const& staticVars = func->staticVars();
661 auto size = staticVars.size();
662 DArrayInit ai(size);
664 for (size_t i = 0; i < staticVars.size(); ++i) {
665 const Func::SVInfo &sv = staticVars[i];
666 auto const staticLocalData = rds::bindStaticLocal(func, sv.name);
667 // FIXME: this should not require variant hops
668 ai.setUnknownKey(
669 VarNR(sv.name),
670 staticLocalData.isInit()
671 ? tvAsCVarRef(staticLocalData.get()->ref.cell())
672 : uninit_variant
675 return ai.toArray();
678 static Array HHVM_METHOD(ReflectionFunctionAbstract, getStaticVariables) {
679 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
680 return get_function_static_variables(func);
683 static String HHVM_METHOD(ReflectionFunctionAbstract, getName) {
684 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
685 auto ret = const_cast<StringData*>(func->name());
686 return String(ret);
689 static bool HHVM_METHOD(ReflectionFunctionAbstract, isHack) {
690 if (RuntimeOption::EnableHipHopSyntax) {
691 return true;
693 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
694 return func->unit()->isHHFile();
697 static bool HHVM_METHOD(ReflectionFunctionAbstract, isInternal) {
698 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
699 return func->isBuiltin();
702 static bool HHVM_METHOD(ReflectionFunctionAbstract, isGenerator) {
703 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
704 return func->isGenerator();
707 static bool HHVM_METHOD(ReflectionFunctionAbstract, isAsync) {
708 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
709 return func->isAsync();
712 static bool HHVM_METHOD(ReflectionFunctionAbstract, isVariadic) {
713 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
714 return func->hasVariadicCaptureParam();
717 static int64_t HHVM_METHOD(ReflectionFunctionAbstract, getNumberOfParameters) {
718 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
719 return func->numParams();
722 // If we are in <?php and in PHP 7 mode w.r.t. scalar types
723 ALWAYS_INLINE static bool isPhpTypeHintEnabled(const Func* func) {
724 return (!(func->unit()->isHHFile() || RuntimeOption::EnableHipHopSyntax) &&
725 RuntimeOption::PHP7_ScalarTypes
729 ALWAYS_INLINE
730 static Array get_function_param_info(const Func* func) {
731 const Func::ParamInfoVec& params = func->params();
732 VArrayInit ai(func->numParams());
734 for (int i = 0; i < func->numParams(); ++i) {
735 Array param = Array::CreateDArray();
736 const Func::ParamInfo& fpi = params[i];
738 param.set(s_index, make_tv<KindOfInt64>(i));
739 param.set(s_name, make_tv<KindOfPersistentString>(func->localNames()[i]));
741 auto const nonExtendedConstraint =
742 fpi.typeConstraint.hasConstraint() &&
743 !fpi.typeConstraint.isExtended();
744 auto const type = nonExtendedConstraint
745 ? fpi.typeConstraint.typeName()
746 : staticEmptyString();
748 param.set(s_type, make_tv<KindOfPersistentString>(type));
749 const StringData* typeHint = fpi.userType
750 ? fpi.userType
751 : staticEmptyString();
752 param.set(s_type_hint, make_tv<KindOfPersistentString>(typeHint));
754 std::string phpTypeHint(isPhpTypeHintEnabled(func) ? typeHint->toCppString() : "");
756 if (!phpTypeHint.empty() && phpTypeHint[0] == '?') {
757 phpTypeHint = phpTypeHint.substr(1);
758 param.set(s_type_hint, phpTypeHint);
761 // callable typehint considered builtin; stdclass typehint is not
762 if (
763 fpi.typeConstraint.isCallable() ||
764 (fpi.typeConstraint.underlyingDataType() &&
765 fpi.typeConstraint.underlyingDataType() != KindOfObject
768 param.set(s_type_hint_builtin, true_varNR.tv());
769 // If we are in <?php and in PHP 7 mode w.r.t. scalar types, then we want
770 // the types to come back as PHP 7 style scalar types, not HH\ style
771 // scalar types.
772 if (!phpTypeHint.empty() && boost::starts_with(phpTypeHint, "HH\\")) {
773 phpTypeHint = phpTypeHint.substr(3);
774 param.set(s_type_hint, phpTypeHint);
776 } else {
777 param.set(s_type_hint_builtin, false_varNR.tv());
779 param.set(s_function, make_tv<KindOfPersistentString>(func->name()));
780 if (func->preClass()) {
781 param.set(
782 s_class,
783 make_tv<KindOfPersistentString>(
784 func->implCls() ? func->implCls()->name()
785 : func->preClass()->name()
789 if (!nonExtendedConstraint || fpi.typeConstraint.isNullable()) {
790 param.set(s_nullable, true_varNR.tv());
791 param.set(s_type_hint_nullable, true_varNR.tv());
792 } else {
793 param.set(s_type_hint_nullable, false_varNR.tv());
796 if (fpi.phpCode) {
797 Variant v = default_arg_from_php_code(fpi, func, i);
798 param.set(s_default, v);
799 param.set(s_defaultText, make_tv<KindOfPersistentString>(fpi.phpCode));
802 if (func->byRef(i)) {
803 param.set(s_ref, true_varNR.tv());
805 if (func->isInOutWrapper() || fpi.inout) {
806 param.set(s_inout, true_varNR.tv());
808 if (fpi.isVariadic()) {
809 param.set(s_is_variadic, true_varNR.tv());
812 Array userAttrs = Array::Create();
813 for (auto it = fpi.userAttributes.begin();
814 it != fpi.userAttributes.end(); ++it) {
815 userAttrs.set(StrNR(it->first), it->second);
817 param.set(s_attributes, VarNR(userAttrs).tv());
819 ai.append(VarNR(param).tv());
822 auto arr = ai.toArray();
824 bool isOptional = true;
825 for (int i = func->numParams() - 1; i >= 0; i--) {
826 auto& param = asArrRef(arr.lvalAt(i));
828 isOptional = isOptional && (param.exists(s_default) ||
829 param.exists(s_is_variadic));
830 param.set(s_is_optional, isOptional);
832 return arr;
835 // helper for getParameters
836 static Array HHVM_METHOD(ReflectionFunctionAbstract, getParamInfo) {
837 // FIXME: each parameter info should be HNI with a handle to the
838 // Func::ParamInfo
839 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
840 return get_function_param_info(func);
843 // helper for getReturnTypeText
844 static String HHVM_METHOD(ReflectionFunctionAbstract, getReturnTypeHint) {
845 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
846 auto retTypeSD = func->returnUserType();
847 if (retTypeSD && retTypeSD->size()) {
848 auto ret = const_cast<StringData*>(retTypeSD);
849 return String(ret);
851 return String();
854 static Array HHVM_METHOD(ReflectionFunctionAbstract, getRetTypeInfo) {
855 Array retTypeInfo = Array::Create();
856 auto name = HHVM_MN(ReflectionFunctionAbstract, getReturnTypeHint)(this_);
857 if (name && !name.empty()) {
858 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
859 auto retType = func->returnTypeConstraint();
860 if (retType.isNullable()) {
861 retTypeInfo.set(s_type_hint_nullable, true_varNR.tv());
862 if (isPhpTypeHintEnabled(func)) {
863 name = name.substr(1); //removes '?' - e.g. ?int -> int
865 } else {
866 retTypeInfo.set(s_type_hint_nullable, false_varNR.tv());
869 if (
870 retType.isCallable() || // callable type hint is considered builtin
871 (retType.underlyingDataType() &&
872 retType.underlyingDataType() != KindOfObject
875 retTypeInfo.set(s_type_hint_builtin, true_varNR.tv());
876 // If we are in <?php and in PHP 7 mode w.r.t. scalar types, then we want
877 // the types to come back as PHP 7 style scalar types, not HH\ style
878 // scalar types.
879 if (isPhpTypeHintEnabled(func) && boost::starts_with(name.toCppString(), "HH\\")) {
880 name = name.substr(3);
882 } else {
883 retTypeInfo.set(s_type_hint_builtin, false_varNR.tv());
885 } else {
886 name = staticEmptyString();
887 retTypeInfo.set(s_type_hint_nullable, false_varNR.tv());
888 retTypeInfo.set(s_type_hint_builtin, false_varNR.tv());
890 retTypeInfo.set(s_type_hint, name);
891 return retTypeInfo;
894 ALWAYS_INLINE
895 static Array get_function_user_attributes(const Func* func) {
896 auto userAttrs = func->userAttributes();
898 DArrayInit ai(userAttrs.size());
899 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
900 ai.set(VarNR::MakeKey(StrNR(it->first).asString()).tv(), it->second);
902 return ai.toArray();
905 static Array HHVM_METHOD(ReflectionFunctionAbstract, getAttributesNamespaced) {
906 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
907 return get_function_user_attributes(func);
910 // ------------------------- class ReflectionMethod
912 // helper for __construct
913 static bool HHVM_METHOD(ReflectionMethod, __init,
914 const Variant& cls_or_object, const String& meth_name) {
915 auto const cls = get_cls(cls_or_object);
916 if (!cls || meth_name.isNull()) {
917 // caller raises exception
918 return false;
920 auto const func = get_method_func(cls, meth_name);
921 if (!func) {
922 // caller raises exception
923 return false;
925 assertx(func->isMethod());
926 ReflectionFuncHandle::Get(this_)->setFunc(func);
927 return true;
930 static bool HHVM_METHOD(ReflectionMethod, isFinal) {
931 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
932 return func->attrs() & AttrFinal;
935 static bool HHVM_METHOD(ReflectionMethod, isAbstract) {
936 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
937 return func->attrs() & AttrAbstract;
940 static bool HHVM_METHOD(ReflectionMethod, isPublic) {
941 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
942 return func->attrs() & AttrPublic;
945 static bool HHVM_METHOD(ReflectionMethod, isProtected) {
946 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
947 return func->attrs() & AttrProtected;
950 static bool HHVM_METHOD(ReflectionMethod, isPrivate) {
951 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
952 return func->attrs() & AttrPrivate;
955 static bool HHVM_METHOD(ReflectionMethod, isStatic) {
956 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
957 return func->attrs() & AttrStatic;
960 static bool HHVM_METHOD(ReflectionMethod, isConstructor) {
961 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
962 return isConstructor(func);
965 static int HHVM_METHOD(ReflectionMethod, getModifiers) {
966 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
967 return get_modifiers(func->attrs(), false, false);
970 // private helper for getPrototype
971 static String HHVM_METHOD(ReflectionMethod, getPrototypeClassname) {
972 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
973 const Class *prototypeCls = nullptr;
974 if (func->baseCls() != nullptr && func->baseCls() != func->implCls()) {
975 prototypeCls = func->baseCls();
976 const Class *result = get_prototype_class_from_interfaces(
977 prototypeCls, func);
978 if (result) { prototypeCls = result; }
979 } else if (func->isMethod()) {
980 // lookup the prototype in the interfaces
981 prototypeCls = get_prototype_class_from_interfaces(func->implCls(), func);
983 if (prototypeCls) {
984 auto ret = const_cast<StringData*>(prototypeCls->name());
985 return String(ret);
987 return String();
990 // private helper for getDeclaringClass
991 static String HHVM_METHOD(ReflectionMethod, getDeclaringClassname) {
992 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
993 auto ret = const_cast<StringData*>(func->implCls()->name());
994 return String(ret);
997 // ------------------------- class ReflectionFile
999 const StaticString s_ReflectionFileHandle("ReflectionFileHandle");
1001 // helper for __construct
1002 static String HHVM_METHOD(ReflectionFile, __init, const String& name) {
1003 if (name.isNull()) {
1004 Reflection::ThrowReflectionExceptionObject(
1005 "Tried to construct ReflectionFile but the name was null"
1009 const Unit* unit = lookupUnit(
1010 File::TranslatePath(name).get(),
1012 nullptr,
1013 Native::s_noNativeFuncs
1016 if (!unit) {
1017 Reflection::ThrowReflectionExceptionObject(folly::sformat(
1018 "File '{}' does not exist",
1019 name
1023 ReflectionFileHandle::Get(this_)->setUnit(unit);
1025 return String::attach(const_cast<StringData*>(unit->filepath()));
1028 static Array HHVM_METHOD(ReflectionFile, getAttributesNamespaced) {
1029 auto const unit = ReflectionFileHandle::GetUnitFor(this_);
1030 assertx(unit);
1032 auto fileAttrs = unit->fileAttributes();
1033 DArrayInit ai(fileAttrs.size());
1035 for (auto it = fileAttrs.begin(); it != fileAttrs.end(); ++it) {
1036 ai.set(StrNR(it->first), tvAsCVarRef(&it->second));
1038 return ai.toArray();
1041 // ------------------------- class ReflectionFunction
1043 // helper for __construct
1044 static bool HHVM_METHOD(ReflectionFunction, __initName, const String& name) {
1045 if (name.isNull()) { return false; }
1046 const Func* func = Unit::loadFunc(name.get());
1047 if (!func) { return false; }
1048 ReflectionFuncHandle::Get(this_)->setFunc(func);
1049 return true;
1052 // helper for __construct
1053 static bool HHVM_METHOD(ReflectionFunction, __initClosure,
1054 const Object& closure) {
1055 auto const cls = get_cls(closure);
1056 assertx(cls);
1057 if (!cls) { return false; }
1058 const Func* func = cls->lookupMethod(s___invoke.get());
1059 if (!func) {
1060 // caller raises exception
1061 return false;
1063 assertx(func->isClosureBody());
1064 assertx(func->implCls()->isScopedClosure());
1065 ReflectionFuncHandle::Get(this_)->setFunc(func);
1066 return true;
1069 const StaticString s_ExpectedClosureInstance("Expected closure instance");
1071 // helper for getClosureScopeClass
1072 static Variant HHVM_METHOD(ReflectionFunction, getClosureScopeClassname,
1073 const Object& closure) {
1074 if (!closure->instanceof(c_Closure::classof())) {
1075 SystemLib::throwExceptionObject(s_ExpectedClosureInstance);
1077 if (auto scope = c_Closure::fromObject(closure.get())->getScope()) {
1078 return String(const_cast<StringData*>(scope->name()));
1080 return init_null_variant;
1083 static Variant HHVM_METHOD(ReflectionFunction, getClosureThisObject,
1084 const Object& closure) {
1085 if (!closure->instanceof(c_Closure::classof())) {
1086 SystemLib::throwExceptionObject(s_ExpectedClosureInstance);
1088 auto const clos = c_Closure::fromObject(closure.get());
1089 if (clos->hasThis()) {
1090 return Object{clos->getThis()};
1092 return init_null_variant;
1095 // helper for getStaticVariables
1096 static Array HHVM_METHOD(ReflectionFunction, getClosureUseVariables,
1097 const Object& closure) {
1098 auto const cls = get_cls(closure);
1099 assertx(cls);
1100 MixedArrayInit ai(cls->numDeclProperties());
1101 auto propVal = closure->propVec();
1102 for (auto const& prop : cls->declProperties()) {
1103 // Closure static locals are represented as special instance properties
1104 // with a mangled name.
1105 if (prop.name->data()[0] == '8') {
1106 static const char prefix[] = "86static_";
1107 assertx(0 == strncmp(prop.name->data(), prefix, sizeof prefix - 1));
1108 String strippedName(prop.name->data() + sizeof prefix - 1,
1109 prop.name->size() - sizeof prefix + 1,
1110 CopyString);
1111 ai.setUnknownKey(VarNR(strippedName), tvAsCVarRef(propVal));
1112 } else {
1113 ai.setWithRef(StrNR(prop.name), *propVal);
1115 propVal++;
1117 return ai.toArray();
1120 /////////////////////////////////////////////////////////////////////////////
1121 // class ReflectionClass
1123 const StaticString s_ReflectionClassHandle("ReflectionClassHandle");
1125 // helper for __construct
1126 static String HHVM_METHOD(ReflectionClass, __init, const String& name) {
1127 return ReflectionClassHandle::Get(this_)->init(name);
1130 static String HHVM_METHOD(ReflectionClass, getName) {
1131 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1132 return cls->nameStr();
1135 static String HHVM_METHOD(ReflectionClass, getParentName) {
1136 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1137 return cls->parentStr();
1140 static bool HHVM_METHOD(ReflectionClass, isHack) {
1141 if (RuntimeOption::EnableHipHopSyntax) {
1142 return true;
1144 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1145 return cls->preClass()->unit()->isHHFile();
1148 static bool HHVM_METHOD(ReflectionClass, isInternal) {
1149 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1150 return cls->attrs() & AttrBuiltin;
1153 static bool HHVM_METHOD(ReflectionClass, isInstantiable) {
1154 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1155 return !(cls->attrs() & (AttrAbstract | AttrInterface | AttrTrait | AttrEnum))
1156 && (cls->getCtor()->attrs() & AttrPublic);
1159 static bool HHVM_METHOD(ReflectionClass, isFinal) {
1160 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1161 return cls->attrs() & AttrFinal;
1164 static bool HHVM_METHOD(ReflectionClass, isAbstract) {
1165 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1166 return cls->attrs() & AttrAbstract;
1169 static bool HHVM_METHOD(ReflectionClass, isInterface) {
1170 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1171 return cls->attrs() & AttrInterface;
1174 static bool HHVM_METHOD(ReflectionClass, isTrait) {
1175 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1176 return cls->attrs() & AttrTrait;
1179 static bool HHVM_METHOD(ReflectionClass, isEnum) {
1180 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1181 return cls->attrs() & AttrEnum;
1184 static int HHVM_METHOD(ReflectionClass, getModifiers) {
1185 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1186 return get_modifiers(cls->attrs(), true, false);
1189 static Variant HHVM_METHOD(ReflectionClass, getFileName) {
1190 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1191 if (cls->attrs() & AttrBuiltin) {
1192 return false_varNR;
1194 auto file = cls->preClass()->unit()->filepath()->data();
1195 if (!file) { file = ""; }
1196 if (file[0] != '/') {
1197 return String(RuntimeOption::SourceRoot + file);
1198 } else {
1199 return String(file);
1203 static Variant HHVM_METHOD(ReflectionClass, getStartLine) {
1204 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1205 if (cls->isBuiltin()) {
1206 return false;
1208 return cls->preClass()->line1();
1211 static Variant HHVM_METHOD(ReflectionClass, getEndLine) {
1212 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1213 if (cls->isBuiltin()) {
1214 return false;
1216 return cls->preClass()->line2();
1219 static Variant HHVM_METHOD(ReflectionClass, getDocComment) {
1220 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1221 auto const pcls = cls->preClass();
1222 auto const comment = pcls->docComment();
1223 if (comment == nullptr || comment->empty()) {
1224 return false_varNR;
1225 } else if (pcls->isBuiltin() && !HHVM_FUNCTION(hphp_debugger_attached)) {
1226 return false_varNR;
1227 } else {
1228 auto ret = const_cast<StringData*>(comment);
1229 return VarNR(ret);
1233 static Array HHVM_METHOD(ReflectionClass, getRequirementNames) {
1234 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1235 if (!(cls->attrs() & (AttrTrait | AttrInterface))) {
1236 // requirements are applied to abstract/concrete classes when they use
1237 // a trait / implement an interface
1238 return empty_varray();
1241 auto const& requirements = cls->allRequirements();
1242 auto numReqs = requirements.size();
1243 if (numReqs == 0) {
1244 return empty_varray();
1247 VArrayInit pai(numReqs);
1248 for (int i = 0; i < numReqs; ++i) {
1249 auto const& req = requirements[i];
1250 pai.append(Variant{const_cast<StringData*>(req->name())});
1252 return pai.toArray();
1255 static Array HHVM_METHOD(ReflectionClass, getInterfaceNames) {
1256 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1258 auto st = req::make<c_Set>();
1259 auto const& allIfaces = cls->allInterfaces();
1260 st->reserve(allIfaces.size());
1262 for (auto const& interface: cls->declInterfaces()) {
1263 st->add(const_cast<StringData*>(interface->name()));
1265 if (allIfaces.size() > cls->declInterfaces().size()) {
1266 for (int i = 0; i < allIfaces.size(); ++i) {
1267 auto const& interface = allIfaces[i];
1268 st->add(const_cast<StringData*>(interface->name()));
1271 return st->toVArray();
1274 static Array HHVM_METHOD(ReflectionClass, getTraitNames) {
1275 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1276 auto const& traits = cls->preClass()->usedTraits();
1277 VArrayInit ai(traits.size());
1278 for (const StringData* traitName : traits) {
1279 ai.append(Variant{const_cast<StringData*>(traitName)});
1281 return ai.toArray();
1284 static Array get_trait_alias_info(const Class* cls) {
1285 auto const& aliases = cls->traitAliases();
1287 if (aliases.size()) {
1288 DArrayInit ai(aliases.size());
1290 for (auto const& namePair : aliases) {
1291 ai.set(StrNR(namePair.first), VarNR(namePair.second).tv());
1293 return ai.toArray();
1294 } else {
1295 // Even if we have alias rules, if we're in repo mode, they will be applied
1296 // during the trait flattening step, and we won't populate traitAliases()
1297 // on the Class.
1298 auto const& rules = cls->preClass()->traitAliasRules();
1300 DArrayInit ai(rules.size());
1302 for (auto const& rule : rules) {
1303 auto namePair = rule.asNamePair();
1304 ai.set(StrNR(namePair.first), VarNR(namePair.second).tv());
1306 return ai.toArray();
1310 static Array HHVM_METHOD(ReflectionClass, getTraitAliases) {
1311 return get_trait_alias_info(ReflectionClassHandle::GetClassFor(this_));
1314 static bool HHVM_METHOD(ReflectionClass, hasMethod, const String& name) {
1315 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1316 return (get_method_func(cls, name) != nullptr);
1319 namespace {
1320 const Class* get_class_from_name(const String& name) {
1321 auto const cls = Unit::loadClass(name.get());
1322 if (!cls) {
1323 auto message = folly::sformat(
1324 "class {} could not be loaded",
1325 name.toCppString()
1327 Reflection::ThrowReflectionExceptionObject(message);
1329 return cls;
1333 // helper for getMethods: returns a Set
1334 static Object HHVM_STATIC_METHOD(
1335 ReflectionClass,
1336 getMethodOrder,
1337 const String& clsname,
1338 int64_t filter) {
1339 auto const cls = get_class_from_name(clsname);
1340 Attr mask = attrs_from_modifiers(filter, false);
1342 // At each step, we fetch from the PreClass is important because the
1343 // order in which getMethods returns matters
1344 req::StringIFastSet visitedMethods;
1345 auto st = req::make<c_Set>();
1346 st->reserve(cls->numMethods());
1348 auto add = [&] (const Func* m) {
1349 if (m->isGenerated()) return;
1350 if (!visitedMethods.insert(m->nameStr()).second) return;
1351 if (m->attrs() & mask) {
1352 st->add(HHVM_FN(strtolower)(m->nameStr()).get());
1356 std::function<void(const Class*)> collect;
1357 std::function<void(const Class*)> collectInterface;
1359 collect = [&] (const Class* clas) {
1360 if (!clas) return;
1362 auto const methods = clas->preClass()->methods();
1363 auto const numMethods = clas->preClass()->numMethods();
1365 auto numDeclMethods = clas->preClass()->numDeclMethods();
1366 if (numDeclMethods == -1) numDeclMethods = numMethods;
1368 // Add declared methods.
1369 for (Slot i = 0; i < numDeclMethods; ++i) {
1370 add(methods[i]);
1373 // Recurse; we need to order the parent's methods before our trait methods.
1374 collect(clas->parent());
1376 for (Slot i = numDeclMethods; i < numMethods; ++i) {
1377 // For repo mode, where trait methods are flattened at compile-time.
1378 add(methods[i]);
1380 for (Slot i = clas->traitsBeginIdx(); i < clas->traitsEndIdx(); ++i) {
1381 // For non-repo mode, where they are added at Class-creation time.
1382 add(clas->getMethod(i));
1386 collectInterface = [&] (const Class* iface) {
1387 if (!iface) return;
1389 size_t const numMethods = iface->preClass()->numMethods();
1390 Func* const* methods = iface->preClass()->methods();
1391 for (Slot i = 0; i < numMethods; ++i) {
1392 add(methods[i]);
1395 for (auto const& parentIface: iface->declInterfaces()) {
1396 collectInterface(parentIface.get());
1398 auto const& allIfaces = iface->allInterfaces();
1399 if (allIfaces.size() > iface->declInterfaces().size()) {
1400 for (int i = 0; i < allIfaces.size(); ++i) {
1401 collectInterface(allIfaces[i].get());
1406 collect(const_cast<Class*>(cls));
1408 // concrete classes should already have all of their methods present
1409 if (((AttrPublic | AttrAbstract | AttrStatic) & mask) &&
1410 cls->attrs() & (AttrInterface | AttrAbstract | AttrTrait)) {
1411 for (auto const& interface: cls->declInterfaces()) {
1412 collectInterface(interface.get());
1414 auto const& allIfaces = cls->allInterfaces();
1415 if (allIfaces.size() > cls->declInterfaces().size()) {
1416 for (int i = 0; i < allIfaces.size(); ++i) {
1417 auto const& interface = allIfaces[i];
1418 collectInterface(interface.get());
1422 return Object(std::move(st));
1425 static bool HHVM_METHOD(ReflectionClass, hasConstant, const String& name) {
1426 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1427 return cls->hasConstant(name.get());
1430 static Variant HHVM_METHOD(ReflectionClass, getConstant, const String& name) {
1431 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1432 auto value = cls->clsCnsGet(name.get());
1433 return (value.m_type == KindOfUninit) ? false_varNR : cellAsCVarRef(value);
1436 static
1437 void addClassConstantNames(const Class* cls,
1438 const req::ptr<c_Set>& st,
1439 size_t limit) {
1440 assertx(cls && st && (st->size() < limit));
1442 auto numConsts = cls->numConstants();
1444 const Class::Const* consts = cls->constants();
1445 for (size_t i = 0; i < numConsts; i++) {
1446 if (consts[i].cls == cls && !consts[i].isAbstract() &&
1447 !consts[i].isType()) {
1448 st->add(const_cast<StringData*>(consts[i].name.get()));
1451 if ((st->size() < limit) && cls->parent()) {
1452 addClassConstantNames(cls->parent(), st, limit);
1455 auto const& allIfaces = cls->allInterfaces();
1456 auto const numIfaces = allIfaces.size();
1457 for (int i = 0; i < numIfaces && (st->size() < limit); ++i) {
1458 addClassConstantNames(allIfaces[i].get(), st, limit);
1462 // helper for getConstants
1463 static Array HHVM_STATIC_METHOD(
1464 ReflectionClass,
1465 getOrderedConstants,
1466 const String& clsname) {
1467 auto const cls = get_class_from_name(clsname);
1469 size_t numConsts = cls->numConstants();
1470 if (!numConsts) {
1471 return Array::CreateDArray();
1474 auto st = req::make<c_Set>();
1475 st->reserve(numConsts);
1477 addClassConstantNames(cls, st, numConsts);
1478 assertx(st->size() <= numConsts);
1480 DArrayInit ai(numConsts);
1481 for (ArrayIter iter(st.get()); iter; ++iter) {
1482 auto constName = iter.first().getStringData();
1483 Cell value = cls->clsCnsGet(constName);
1484 assertx(value.m_type != KindOfUninit);
1485 ai.add(constName, cellAsCVarRef(value));
1487 return ai.toArray();
1490 // helper for getAbstractConstantNames
1491 static Array HHVM_STATIC_METHOD(
1492 ReflectionClass,
1493 getOrderedAbstractConstants,
1494 const String& clsname) {
1495 auto const cls = get_class_from_name(clsname);
1497 size_t numConsts = cls->numConstants();
1498 if (!numConsts) {
1499 return Array::CreateDArray();
1502 auto st = req::make<c_Set>();
1503 st->reserve(numConsts);
1505 const Class::Const* consts = cls->constants();
1506 for (size_t i = 0; i < numConsts; i++) {
1507 if (consts[i].isAbstract() && !consts[i].isType()) {
1508 st->add(const_cast<StringData*>(consts[i].name.get()));
1512 assertx(st->size() <= numConsts);
1514 return st->toDArray();
1519 // helper for getTypeConstants/hasTypeConstant
1520 static Array HHVM_STATIC_METHOD(
1521 ReflectionClass,
1522 getOrderedTypeConstants,
1523 const String& clsname) {
1524 auto const cls = get_class_from_name(clsname);
1526 size_t numConsts = cls->numConstants();
1527 if (!numConsts) {
1528 return Array::Create();
1531 auto st = req::make<c_Set>();
1532 st->reserve(numConsts);
1534 const Class::Const* consts = cls->constants();
1535 for (size_t i = 0; i < numConsts; i++) {
1536 if (consts[i].isType()) {
1537 st->add(const_cast<StringData*>(consts[i].name.get()));
1541 assertx(st->size() <= numConsts);
1542 return st->toDArray();
1545 static Array HHVM_METHOD(ReflectionClass, getAttributesNamespaced) {
1546 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1547 // UserAttributes are stored exclusively on the PreClass.
1548 auto const pcls = cls->preClass();
1550 auto userAttrs = pcls->userAttributes();
1551 DArrayInit ai(userAttrs.size());
1553 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
1554 ai.set(StrNR(it->first), tvAsCVarRef(&it->second));
1556 return ai.toArray();
1559 static Array HHVM_METHOD(ReflectionClass, getAttributesRecursiveNamespaced) {
1560 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1562 Array ret = Array::CreateDArray(); // no reasonable idea about sizing
1564 // UserAttributes are stored in the PreClass, so we must walk the parent
1565 // chain to get all of them; attribute specifications from child classes
1566 // win over parents.
1568 // const pointer to Class => pointer to a (const) Class
1569 Class* currentCls = const_cast<Class*>(cls);
1570 do {
1571 auto const pcls = currentCls->preClass();
1572 for (auto it = pcls->userAttributes().begin();
1573 it != pcls->userAttributes().end(); ++it) {
1574 if (!ret.exists(StrNR(it->first))) {
1575 ret.set(StrNR(it->first), tvAsCVarRef(&it->second));
1578 } while ((currentCls = currentCls->parent()));
1580 return ret;
1583 static Array HHVM_STATIC_METHOD(
1584 ReflectionClass,
1585 getClassPropertyInfo,
1586 const String& clsname) {
1588 * FIXME: This implementation is pretty horrible and should be rewritten
1589 * when ReflectionProperty is ported.
1591 auto const cls = get_class_from_name(clsname);
1592 auto const properties = cls->declProperties();
1593 cls->initialize();
1594 auto const& propInitVec = cls->getPropData()
1595 ? *cls->getPropData()
1596 : cls->declPropInit();
1598 auto ret = Array::Create();
1599 for (auto const& declProp : properties) {
1600 auto slot = declProp.serializationIdx;
1601 auto const& prop = properties[slot];
1602 auto const& default_val = tvAsCVarRef(&propInitVec[slot]);
1603 if (((prop.attrs & AttrPrivate) == AttrPrivate) && (prop.cls != cls)) {
1604 continue;
1607 auto info = Array::Create();
1608 set_instance_prop_info(info, &prop, default_val);
1609 ret.set(StrNR(prop.name), VarNR(info).tv());
1612 // static properties
1613 auto const sProperties = cls->staticProperties();
1614 for (auto const& sProp : sProperties) {
1615 auto slot = sProp.serializationIdx;
1616 auto const& prop = sProperties[slot];
1617 if (((prop.attrs & AttrPrivate) == AttrPrivate) && (prop.cls != cls)) {
1618 continue;
1621 auto info = Array::Create();
1622 set_static_prop_info(info, &prop);
1623 ret.set(StrNR(prop.name), VarNR(info).tv());
1625 return ret;
1628 static Array HHVM_METHOD(ReflectionClass, getDynamicPropertyInfos,
1629 const Object& obj) {
1630 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1631 auto obj_data = obj.get();
1632 assertx(obj_data->getVMClass() == cls);
1633 if (!obj_data->hasDynProps()) {
1634 return empty_array();
1637 auto const dynPropArray = obj_data->dynPropArray();
1638 ArrayInit ret(dynPropArray->size(), ArrayInit::Mixed{});
1639 IterateKV(dynPropArray.get(), [&](Cell k, TypedValue) {
1640 if (RuntimeOption::EvalNoticeOnReadDynamicProp) {
1641 auto const key = tvCastToString(k);
1642 obj_data->raiseReadDynamicProp(key.get());
1644 Array info = Array::Create();
1645 set_dyn_prop_info(info, k, cls->name());
1646 ret.setValidKey(k, VarNR(info).tv());
1648 return ret.toArray();
1651 static String HHVM_METHOD(ReflectionClass, getConstructorName) {
1652 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1653 auto ctor = cls->getDeclaredCtor();
1654 if (!ctor) { return String(); }
1655 auto ret = const_cast<StringData*>(ctor->name());
1656 return String(ret);
1659 void ReflectionClassHandle::wakeup(const Variant& content, ObjectData* obj) {
1660 if (!content.isString()) {
1661 throw Exception("Native data of ReflectionClass should be a class name");
1664 String clsName = content.toString();
1665 String result = init(clsName);
1666 if (result.empty()) {
1667 auto msg = folly::format("Class {} does not exist", clsName).str();
1668 Reflection::ThrowReflectionExceptionObject(String(msg));
1671 // It is possible that $name does not get serialized. If a class derives
1672 // from ReflectionClass and the return value of its __sleep() function does
1673 // not contain 'name', $name gets ignored. So, we restore $name here.
1674 obj->setProp(nullptr, s_name.get(), result.toCell());
1677 static Variant reflection_extension_name_get(const Object& this_) {
1678 assertx(Reflection::s_ReflectionExtensionClass);
1679 auto const name = this_->getProp(
1680 Reflection::s_ReflectionExtensionClass,
1681 s___name.get()
1682 ).unboxed();
1683 return tvCastToString(name.tv());
1686 static Native::PropAccessor reflection_extension_Accessors[] = {
1687 {"name", reflection_extension_name_get,
1688 nullptr, nullptr, nullptr}, // name is read only
1689 {nullptr, nullptr, nullptr, nullptr, nullptr}
1692 static Native::PropAccessorMap reflection_extension_accessorsMap
1693 ((Native::PropAccessor*)reflection_extension_Accessors);
1695 struct reflection_extension_PropHandler :
1696 Native::MapPropHandler<reflection_extension_PropHandler> {
1698 static constexpr Native::PropAccessorMap& map =
1699 reflection_extension_accessorsMap;
1702 /////////////////////////////////////////////////////////////////////////////
1703 // class ReflectionTypeConstant
1705 const StaticString s_ReflectionConstHandle("ReflectionConstHandle");
1707 // helper for __construct
1708 static bool HHVM_METHOD(ReflectionTypeConstant, __init,
1709 const Variant& cls_or_obj, const String& const_name) {
1710 auto const cls = get_cls(cls_or_obj);
1711 if (!cls || const_name.isNull()) {
1712 // caller raises exception
1713 return false;
1716 size_t numConsts = cls->numConstants();
1717 const Class::Const* consts = cls->constants();
1719 for (size_t i = 0; i < numConsts; i++) {
1720 if (const_name.same(consts[i].name) && consts[i].isType()) {
1721 auto handle = ReflectionConstHandle::Get(this_);
1722 handle->setConst(&consts[i]);
1723 handle->setClass(cls);
1724 return true;
1728 // caller raises exception
1729 return false;
1732 static String HHVM_METHOD(ReflectionTypeConstant, getName) {
1733 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1734 auto ret = const_cast<StringData*>(cns->name.get());
1735 return String(ret);
1738 static bool HHVM_METHOD(ReflectionTypeConstant, isAbstract) {
1739 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1740 return cns->isAbstract();
1743 // helper for getAssignedTypeText
1744 static String HHVM_METHOD(ReflectionTypeConstant, getAssignedTypeHint) {
1745 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1747 if (isStringType(cns->val.m_type)) {
1748 return String(cns->val.m_data.pstr);
1751 if (isArrayLikeType(cns->val.m_type)) {
1752 auto const cls = cns->cls;
1753 // go to the preclass to find the unresolved TypeStructure to get
1754 // the original assigned type text
1755 auto const preCls = cls->preClass();
1756 auto typeCns = preCls->lookupConstant(cns->name);
1757 assertx(typeCns->isType());
1758 assertx(!typeCns->isAbstract());
1759 assertx(isArrayLikeType(typeCns->val().m_type));
1760 return TypeStructure::toString(Array::attach(typeCns->val().m_data.parr));
1763 return String();
1766 // private helper for getDeclaringClass
1767 static String HHVM_METHOD(ReflectionTypeConstant, getDeclaringClassname) {
1768 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1769 auto cls = cns->cls;
1770 auto ret = const_cast<StringData*>(cls->name());
1771 return String(ret);
1774 // private helper for getClass
1775 static String HHVM_METHOD(ReflectionTypeConstant, getClassname) {
1776 auto const cls = ReflectionConstHandle::GetClassFor(this_);
1777 auto ret = const_cast<StringData*>(cls->name());
1778 return String(ret);
1781 /////////////////////////////////////////////////////////////////////////////
1782 // class ReflectionProperty
1784 const StaticString s_ReflectionPropHandle("ReflectionPropHandle");
1786 static void HHVM_METHOD(ReflectionProperty, __construct,
1787 const Variant& cls_or_obj, const String& prop_name) {
1788 auto const cls = get_cls(cls_or_obj);
1789 if (!cls) {
1790 Reflection::ThrowReflectionExceptionObject(folly::sformat(
1791 "Class {} does not exist",
1792 cls_or_obj.toString().toCppString()
1795 if (prop_name.isNull()) {
1796 Reflection::ThrowReflectionExceptionObject(folly::sformat(
1797 "Property {}:: does not exist",
1798 cls->name()->toCppString()
1802 auto data = Native::data<ReflectionPropHandle>(this_);
1804 // is there a declared instance property?
1805 auto lookup = cls->getDeclPropIndex(cls, prop_name.get());
1806 auto propIdx = lookup.slot;
1807 if (propIdx != kInvalidSlot) {
1808 auto const prop = &cls->declProperties()[propIdx];
1809 data->setInstanceProp(prop);
1810 this_->setProp(nullptr, s_class.get(),
1811 make_tv<KindOfPersistentString>(prop->cls->name()));
1812 this_->setProp(nullptr, s_name.get(),
1813 make_tv<KindOfPersistentString>(prop->name));
1814 return;
1817 // is there a declared static property?
1818 lookup = cls->findSProp(cls, prop_name.get());
1819 propIdx = lookup.slot;
1820 if (propIdx != kInvalidSlot) {
1821 auto const prop = &cls->staticProperties()[propIdx];
1822 data->setStaticProp(prop);
1823 this_->setProp(nullptr, s_class.get(),
1824 make_tv<KindOfPersistentString>(prop->cls->name()));
1825 this_->setProp(nullptr, s_name.get(),
1826 make_tv<KindOfPersistentString>(prop->name));
1827 return;
1830 // is there a dynamic property?
1831 if (cls_or_obj.is(KindOfObject)) {
1832 auto obj = cls_or_obj.toCObjRef().get();
1833 assertx(cls == obj->getVMClass());
1834 if (obj->getAttribute(ObjectData::HasDynPropArr) &&
1835 obj->dynPropArray().exists(
1836 obj->dynPropArray().convertKey<IntishCast::CastSilently>(prop_name))
1838 if (RuntimeOption::EvalNoticeOnReadDynamicProp) {
1839 obj->raiseReadDynamicProp(prop_name.get());
1841 data->setDynamicProp();
1842 this_->setProp(nullptr, s_class.get(),
1843 make_tv<KindOfPersistentString>(cls->name()));
1844 this_->setProp(nullptr, s_name.get(), prop_name.toCell());
1845 return;
1849 Reflection::ThrowReflectionExceptionObject(folly::sformat(
1850 "Property {}::{} does not exist",
1851 cls->name()->toCppString(),
1852 prop_name.toCppString()
1856 namespace {
1858 [[noreturn]] void reflection_property_internal_error() {
1859 raise_fatal_error("Internal error: Failed to retrieve the reflection object");
1864 static bool HHVM_METHOD(ReflectionProperty, isPublic) {
1865 auto const data = Native::data<ReflectionPropHandle>(this_);
1866 switch (data->getType()) {
1867 case ReflectionPropHandle::Type::Instance:
1868 return data->getProp()->attrs & AttrPublic;
1869 case ReflectionPropHandle::Type::Static:
1870 return data->getSProp()->attrs & AttrPublic;
1871 case ReflectionPropHandle::Type::Dynamic:
1872 return true;
1873 default:
1874 reflection_property_internal_error();
1878 static bool HHVM_METHOD(ReflectionProperty, isProtected) {
1879 auto const data = Native::data<ReflectionPropHandle>(this_);
1880 switch (data->getType()) {
1881 case ReflectionPropHandle::Type::Instance:
1882 return data->getProp()->attrs & AttrProtected;
1883 case ReflectionPropHandle::Type::Static:
1884 return data->getSProp()->attrs & AttrProtected;
1885 case ReflectionPropHandle::Type::Dynamic:
1886 return false;
1887 default:
1888 reflection_property_internal_error();
1892 static bool HHVM_METHOD(ReflectionProperty, isPrivate) {
1893 auto const data = Native::data<ReflectionPropHandle>(this_);
1894 switch (data->getType()) {
1895 case ReflectionPropHandle::Type::Instance:
1896 return data->getProp()->attrs & AttrPrivate;
1897 case ReflectionPropHandle::Type::Static:
1898 return data->getSProp()->attrs & AttrPrivate;
1899 case ReflectionPropHandle::Type::Dynamic:
1900 return false;
1901 default:
1902 reflection_property_internal_error();
1906 static bool HHVM_METHOD(ReflectionProperty, isStatic) {
1907 auto const data = Native::data<ReflectionPropHandle>(this_);
1908 switch (data->getType()) {
1909 case ReflectionPropHandle::Type::Static:
1910 return true;
1911 case ReflectionPropHandle::Type::Instance:
1912 case ReflectionPropHandle::Type::Dynamic:
1913 return false;
1914 default:
1915 reflection_property_internal_error();
1919 static bool HHVM_METHOD(ReflectionProperty, isDefault) {
1920 auto const data = Native::data<ReflectionPropHandle>(this_);
1921 switch (data->getType()) {
1922 case ReflectionPropHandle::Type::Instance:
1923 case ReflectionPropHandle::Type::Static:
1924 return true;
1925 case ReflectionPropHandle::Type::Dynamic:
1926 return false;
1927 default:
1928 reflection_property_internal_error();
1932 static int HHVM_METHOD(ReflectionProperty, getModifiers) {
1933 auto const data = Native::data<ReflectionPropHandle>(this_);
1934 switch (data->getType()) {
1935 case ReflectionPropHandle::Type::Instance:
1936 return get_modifiers(data->getProp()->attrs, false, true);
1937 case ReflectionPropHandle::Type::Static:
1938 return get_modifiers(data->getSProp()->attrs, false, true);
1939 case ReflectionPropHandle::Type::Dynamic:
1940 return get_modifiers(AttrPublic, false, true);
1941 default:
1942 reflection_property_internal_error();
1946 static TypedValue HHVM_METHOD(ReflectionProperty, getDocComment) {
1947 auto const data = Native::data<ReflectionPropHandle>(this_);
1948 const StringData *comment = nullptr;
1949 switch (data->getType()) {
1950 case ReflectionPropHandle::Type::Instance:
1951 comment = data->getProp()->preProp->docComment();
1952 break;
1953 case ReflectionPropHandle::Type::Static:
1954 comment = data->getSProp()->preProp->docComment();
1955 break;
1956 case ReflectionPropHandle::Type::Dynamic:
1957 break;
1958 default:
1959 reflection_property_internal_error();
1961 if (comment == nullptr || comment->empty()) {
1962 return tvReturn(false);
1963 } else {
1964 Variant vComment{comment, Variant::PersistentStrInit{}};
1965 return tvReturn(std::move(vComment));
1969 static String HHVM_METHOD(ReflectionProperty, getTypeText) {
1970 auto const data = Native::data<ReflectionPropHandle>(this_);
1971 const StringData *type = nullptr;
1972 switch (data->getType()) {
1973 case ReflectionPropHandle::Type::Instance:
1974 type = data->getProp()->preProp->userType();
1975 break;
1976 case ReflectionPropHandle::Type::Static:
1977 type = data->getSProp()->preProp->userType();
1978 break;
1979 case ReflectionPropHandle::Type::Dynamic:
1980 break;
1981 default:
1982 reflection_property_internal_error();
1984 if (type == nullptr || type->empty()) {
1985 return empty_string();
1986 } else {
1987 return StrNR(type);
1991 static TypedValue HHVM_METHOD(ReflectionProperty, getDefaultValue) {
1992 auto const data = Native::data<ReflectionPropHandle>(this_);
1993 switch (data->getType()) {
1994 case ReflectionPropHandle::Type::Instance: {
1995 auto const prop = data->getProp();
1996 // We can't get propIdx from prop->idx (that's not what that is) or by
1997 // doing prop - prop->cls->declProperties().begin() (the prop can be in
1998 // the prop vector of a child class but it will always point to the class
1999 // it was declared in); so if we don't want to store propIdx we have to
2000 // look it up by name.
2001 auto lookup = prop->cls->getDeclPropIndex(prop->cls, prop->name);
2002 auto propIdx = lookup.slot;
2003 assertx(propIdx != kInvalidSlot);
2004 prop->cls->initialize();
2005 auto const& propInitVec = prop->cls->getPropData()
2006 ? *prop->cls->getPropData()
2007 : prop->cls->declPropInit();
2008 return tvReturn(tvAsCVarRef(&propInitVec[propIdx]));
2010 case ReflectionPropHandle::Type::Static: {
2011 auto const prop = data->getSProp();
2012 prop->cls->initialize();
2013 return tvReturn(tvAsCVarRef(&prop->val));
2015 case ReflectionPropHandle::Type::Dynamic:
2016 return make_tv<KindOfNull>();
2017 default:
2018 reflection_property_internal_error();
2022 static Array HHVM_METHOD(ReflectionProperty, getAttributesNamespaced) {
2023 auto const data = Native::data<ReflectionPropHandle>(this_);
2024 auto attrs = Array::CreateDict();
2025 switch (data->getType()) {
2026 case ReflectionPropHandle::Type::Instance: {
2027 auto const prop = data->getProp()->preProp;
2028 for (auto attr : prop->userAttributes()) {
2029 attrs.set(StrNR(attr.first), attr.second);
2031 return attrs;
2033 case ReflectionPropHandle::Type::Static: {
2034 auto const prop = data->getSProp()->preProp;
2035 for (auto attr : prop->userAttributes()) {
2036 attrs.set(StrNR(attr.first), attr.second);
2038 return attrs;
2040 case ReflectionPropHandle::Type::Dynamic:
2041 return attrs;
2042 default:
2043 reflection_property_internal_error();
2047 /////////////////////////////////////////////////////////////////////////////
2048 // class ReflectionTypeAlias
2050 const StaticString s_ReflectionTypeAliasHandle("ReflectionTypeAliasHandle");
2052 // helper for __construct:
2053 // caller throws exception when return value is false
2054 static String HHVM_METHOD(ReflectionTypeAlias, __init, const String& name) {
2055 auto const typeAliasReq = Unit::loadTypeAlias(name.get());
2057 if (!typeAliasReq) {
2058 return empty_string();
2061 ReflectionTypeAliasHandle::Get(this_)->setTypeAliasReq(typeAliasReq);
2062 return String::attach(const_cast<StringData*>(typeAliasReq->name.get()));
2065 static Array HHVM_METHOD(ReflectionTypeAlias, getTypeStructure) {
2066 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
2067 assertx(req);
2068 auto const typeStructure = req->typeStructure;
2069 assertx(!typeStructure.empty());
2070 assertx(typeStructure.isDictOrDArray());
2071 return typeStructure;
2074 static String HHVM_METHOD(ReflectionTypeAlias, getAssignedTypeText) {
2075 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
2076 assertx(req);
2077 auto const typeStructure = req->typeStructure;
2078 assertx(!typeStructure.empty());
2079 assertx(typeStructure.isDictOrDArray());
2080 return TypeStructure::toString(typeStructure);
2083 static Array HHVM_METHOD(ReflectionTypeAlias, getAttributesNamespaced) {
2084 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
2085 assertx(req);
2086 auto const userAttrs = req->userAttrs;
2088 DArrayInit ai(userAttrs.size());
2089 for (auto& attr : userAttrs) {
2090 ai.set(StrNR(attr.first), tvAsCVarRef(&attr.second));
2092 return ai.toArray();
2095 static String HHVM_METHOD(ReflectionTypeAlias, getFileName) {
2096 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
2097 assertx(req);
2098 auto file = req->unit->filepath()->data();
2099 if (!file) { file = ""; }
2100 if (file[0] != '/') {
2101 return String(RuntimeOption::SourceRoot + file);
2102 } else {
2103 return String(file);
2107 ///////////////////////////////////////////////////////////////////////////////
2108 struct ReflectionExtension final : Extension {
2109 ReflectionExtension() : Extension("reflection", "$Id$") { }
2110 void moduleInit() override {
2111 HHVM_FE(hphp_create_object);
2112 HHVM_FE(hphp_create_object_without_constructor);
2113 HHVM_FE(hphp_get_extension_info);
2114 HHVM_FE(hphp_get_original_class_name);
2115 HHVM_FE(hphp_get_property);
2116 HHVM_FE(hphp_get_static_property);
2117 HHVM_FE(hphp_invoke);
2118 HHVM_FE(hphp_invoke_method);
2119 HHVM_FE(hphp_set_property);
2120 HHVM_FE(hphp_set_static_property);
2121 HHVM_FALIAS(HH\\type_structure, type_structure);
2123 HHVM_ME(ReflectionFunctionAbstract, getName);
2124 HHVM_ME(ReflectionFunctionAbstract, isHack);
2125 HHVM_ME(ReflectionFunctionAbstract, isInternal);
2126 HHVM_ME(ReflectionFunctionAbstract, isGenerator);
2127 HHVM_ME(ReflectionFunctionAbstract, isAsync);
2128 HHVM_ME(ReflectionFunctionAbstract, isVariadic);
2129 HHVM_ME(ReflectionFunctionAbstract, getFileName);
2130 HHVM_ME(ReflectionFunctionAbstract, getStartLine);
2131 HHVM_ME(ReflectionFunctionAbstract, getEndLine);
2132 HHVM_ME(ReflectionFunctionAbstract, getDocComment);
2133 HHVM_ME(ReflectionFunctionAbstract, getStaticVariables);
2134 HHVM_ME(ReflectionFunctionAbstract, getReturnTypeHint);
2135 HHVM_ME(ReflectionFunctionAbstract, getNumberOfParameters);
2136 HHVM_ME(ReflectionFunctionAbstract, getParamInfo);
2137 HHVM_ME(ReflectionFunctionAbstract, getAttributesNamespaced);
2138 HHVM_ME(ReflectionFunctionAbstract, getRetTypeInfo);
2140 HHVM_ME(ReflectionMethod, __init);
2141 HHVM_ME(ReflectionMethod, isFinal);
2142 HHVM_ME(ReflectionMethod, isAbstract);
2143 HHVM_ME(ReflectionMethod, isPublic);
2144 HHVM_ME(ReflectionMethod, isProtected);
2145 HHVM_ME(ReflectionMethod, isPrivate);
2146 HHVM_ME(ReflectionMethod, isStatic);
2147 HHVM_ME(ReflectionMethod, isConstructor);
2148 HHVM_ME(ReflectionMethod, getModifiers);
2149 HHVM_ME(ReflectionMethod, getPrototypeClassname);
2150 HHVM_ME(ReflectionMethod, getDeclaringClassname);
2152 HHVM_ME(ReflectionFile, __init);
2153 HHVM_ME(ReflectionFile, getAttributesNamespaced);
2155 HHVM_ME(ReflectionFunction, __initName);
2156 HHVM_ME(ReflectionFunction, __initClosure);
2157 HHVM_ME(ReflectionFunction, getClosureUseVariables);
2158 HHVM_ME(ReflectionFunction, getClosureScopeClassname);
2159 HHVM_ME(ReflectionFunction, getClosureThisObject);
2161 HHVM_ME(ReflectionTypeConstant, __init);
2162 HHVM_ME(ReflectionTypeConstant, getName);
2163 HHVM_ME(ReflectionTypeConstant, isAbstract);
2164 HHVM_ME(ReflectionTypeConstant, getAssignedTypeHint);
2165 HHVM_ME(ReflectionTypeConstant, getDeclaringClassname);
2166 HHVM_ME(ReflectionTypeConstant, getClassname);
2168 HHVM_ME(ReflectionProperty, __construct);
2169 HHVM_ME(ReflectionProperty, isPublic);
2170 HHVM_ME(ReflectionProperty, isProtected);
2171 HHVM_ME(ReflectionProperty, isPrivate);
2172 HHVM_ME(ReflectionProperty, isStatic);
2173 HHVM_ME(ReflectionProperty, isDefault);
2174 HHVM_ME(ReflectionProperty, getModifiers);
2175 HHVM_ME(ReflectionProperty, getDocComment);
2176 HHVM_ME(ReflectionProperty, getTypeText);
2177 HHVM_ME(ReflectionProperty, getDefaultValue);
2178 HHVM_ME(ReflectionProperty, getAttributesNamespaced);
2180 HHVM_ME(ReflectionTypeAlias, __init);
2181 HHVM_ME(ReflectionTypeAlias, getTypeStructure);
2182 HHVM_ME(ReflectionTypeAlias, getAttributesNamespaced);
2183 HHVM_ME(ReflectionTypeAlias, getAssignedTypeText);
2184 HHVM_ME(ReflectionTypeAlias, getFileName);
2186 HHVM_ME(ReflectionClass, __init);
2187 HHVM_ME(ReflectionClass, getName);
2188 HHVM_ME(ReflectionClass, getParentName);
2189 HHVM_ME(ReflectionClass, isHack);
2190 HHVM_ME(ReflectionClass, isInternal);
2191 HHVM_ME(ReflectionClass, isInstantiable);
2192 HHVM_ME(ReflectionClass, isInterface);
2193 HHVM_ME(ReflectionClass, isTrait);
2194 HHVM_ME(ReflectionClass, isEnum);
2195 HHVM_ME(ReflectionClass, isAbstract);
2196 HHVM_ME(ReflectionClass, isFinal);
2197 HHVM_ME(ReflectionClass, getModifiers);
2198 HHVM_ME(ReflectionClass, getFileName);
2199 HHVM_ME(ReflectionClass, getStartLine);
2200 HHVM_ME(ReflectionClass, getEndLine);
2201 HHVM_ME(ReflectionClass, getDocComment);
2202 HHVM_ME(ReflectionClass, getInterfaceNames);
2203 HHVM_ME(ReflectionClass, getRequirementNames);
2204 HHVM_ME(ReflectionClass, getTraitNames);
2205 HHVM_ME(ReflectionClass, getTraitAliases);
2207 HHVM_ME(ReflectionClass, hasMethod);
2208 HHVM_STATIC_ME(ReflectionClass, getMethodOrder);
2210 HHVM_ME(ReflectionClass, hasConstant);
2211 HHVM_ME(ReflectionClass, getConstant);
2212 HHVM_STATIC_ME(ReflectionClass, getOrderedConstants);
2213 HHVM_STATIC_ME(ReflectionClass, getOrderedAbstractConstants);
2214 HHVM_STATIC_ME(ReflectionClass, getOrderedTypeConstants);
2216 HHVM_ME(ReflectionClass, getAttributesNamespaced);
2217 HHVM_ME(ReflectionClass, getAttributesRecursiveNamespaced);
2219 HHVM_STATIC_ME(ReflectionClass, getClassPropertyInfo);
2220 HHVM_ME(ReflectionClass, getDynamicPropertyInfos);
2221 HHVM_ME(ReflectionClass, getConstructorName);
2223 Native::registerNativeDataInfo<ReflectionFuncHandle>(
2224 s_ReflectionFuncHandle.get());
2225 Native::registerNativeDataInfo<ReflectionClassHandle>(
2226 s_ReflectionClassHandle.get());
2227 Native::registerNativeDataInfo<ReflectionConstHandle>(
2228 s_ReflectionConstHandle.get());
2229 Native::registerNativeDataInfo<ReflectionPropHandle>(
2230 s_ReflectionPropHandle.get());
2231 Native::registerNativeDataInfo<ReflectionFileHandle>(
2232 s_ReflectionFileHandle.get());
2233 Native::registerNativeDataInfo<ReflectionTypeAliasHandle>(
2234 s_ReflectionTypeAliasHandle.get(), Native::NO_SWEEP);
2236 Native::registerNativePropHandler
2237 <reflection_extension_PropHandler>(s_reflectionextension);
2239 loadSystemlib();
2240 loadSystemlib("reflection-classes");
2241 loadSystemlib("reflection-internals-functions");
2242 loadSystemlib("reflection_hni");
2244 Reflection::s_ReflectionExceptionClass =
2245 Unit::lookupClass(s_reflectionexception.get());
2246 assertx(Reflection::s_ReflectionExceptionClass);
2247 Reflection::s_ReflectionExtensionClass =
2248 Unit::lookupClass(s_reflectionextension.get());
2249 assertx(Reflection::s_ReflectionExtensionClass);
2251 } s_reflection_extension;
2253 ///////////////////////////////////////////////////////////////////////////////
2255 namespace DebuggerReflection {
2257 static bool set_debugger_source_info(Array &ret, const char *file, int line1,
2258 int line2) {
2259 if (!file) file = "";
2260 if (file[0] != '/') {
2261 ret.set(s_file, String(RuntimeOption::SourceRoot + file));
2262 } else {
2263 ret.set(s_file, file);
2265 ret.set(s_line1, make_tv<KindOfInt64>(line1));
2266 ret.set(s_line2, make_tv<KindOfInt64>(line2));
2267 return file && *file;
2270 static void set_debugger_return_type_constraint(Array &ret, const StringData* retType) {
2271 if (retType && retType->size()) {
2272 assertx(!retType->isRefCounted());
2273 ret.set(s_return_type, make_tv<KindOfPersistentString>(retType));
2274 } else {
2275 ret.set(s_return_type, false_varNR.tv());
2279 static void set_debugger_reflection_method_prototype_info(Array& ret,
2280 const Func *func) {
2281 const Class *prototypeCls = nullptr;
2282 if (func->baseCls() != nullptr && func->baseCls() != func->implCls()) {
2283 prototypeCls = func->baseCls();
2284 const Class *result = get_prototype_class_from_interfaces(
2285 prototypeCls, func);
2286 if (result) prototypeCls = result;
2287 } else if (func->isMethod()) {
2288 // lookup the prototype in the interfaces
2289 prototypeCls = get_prototype_class_from_interfaces(func->implCls(), func);
2291 if (prototypeCls) {
2292 Array prototype = Array::Create();
2293 prototype.set(s_class,
2294 make_tv<KindOfPersistentString>(prototypeCls->name()));
2295 prototype.set(s_name, make_tv<KindOfPersistentString>(func->name()));
2296 ret.set(s_prototype, prototype);
2300 static void set_debugger_reflection_function_info(Array& ret,
2301 const Func* func) {
2302 // return type
2303 if (func->isBuiltin()) {
2304 ret.set(s_internal, true_varNR.tv());
2306 set_debugger_return_type_constraint(ret, func->returnUserType());
2308 // doc comments
2309 set_doc_comment(ret, func->docComment(), func->isBuiltin());
2311 // parameters
2312 ret.set(s_params, get_function_param_info(func));
2314 // static variables
2315 ret.set(s_static_variables, get_function_static_variables(func));
2317 // user attributes
2318 ret.set(s_attributes, get_function_user_attributes(func));
2320 ret.set(s_is_async, func->isAsync());
2321 ret.set(s_is_closure, func->isClosureBody());
2322 ret.set(s_is_generator, func->isGenerator());
2325 static void set_debugger_reflection_method_info(Array& ret, const Func* func,
2326 const Class* cls) {
2327 ret.set(s_name, make_tv<KindOfPersistentString>(func->name()));
2328 set_attrs(ret, get_modifiers(func->attrs(), false, false));
2330 if (isConstructor(func)) {
2331 ret.set(s_constructor, true_varNR.tv());
2334 // If Func* is from a PreClass, it doesn't know about base classes etc.
2335 // Swap it out for the full version if possible.
2336 auto resolved_func = func->implCls()
2337 ? func
2338 : cls->lookupMethod(func->name());
2340 if (!resolved_func) {
2341 resolved_func = func;
2344 ret.set(s_class,
2345 make_tv<KindOfPersistentString>(resolved_func->implCls()->name()));
2346 set_debugger_reflection_function_info(ret, resolved_func);
2347 set_debugger_source_info(ret, func->unit()->filepath()->data(),
2348 func->line1(), func->line2());
2349 set_debugger_reflection_method_prototype_info(ret, resolved_func);
2352 Array get_function_info(const String& name) {
2353 Array ret;
2354 if (name.get() == nullptr) return ret;
2355 const Func* func = Unit::loadFunc(name.get());
2356 if (!func) return ret;
2357 ret.set(s_name, make_tv<KindOfPersistentString>(func->name()));
2359 // setting parameters and static variables
2360 set_debugger_reflection_function_info(ret, func);
2361 set_debugger_source_info(ret, func->unit()->filepath()->data(),
2362 func->line1(), func->line2());
2363 return ret;
2366 Array get_class_info(const String& name) {
2367 auto cls = get_cls(name);
2368 if (!cls) return Array();
2370 Array ret;
2371 ret.set(s_name, make_tv<KindOfPersistentString>(cls->name()));
2372 ret.set(s_extension, empty_string_variant_ref);
2373 ret.set(s_parent, make_tv<KindOfPersistentString>(cls->preClass()->parent()));
2375 // interfaces
2377 Array arr = Array::Create();
2378 for (auto const& interface: cls->declInterfaces()) {
2379 arr.set(interface->nameStr(), make_tv<KindOfInt64>(1));
2381 auto const& allIfaces = cls->allInterfaces();
2382 if (allIfaces.size() > cls->declInterfaces().size()) {
2383 for (int i = 0; i < allIfaces.size(); ++i) {
2384 auto const& interface = allIfaces[i];
2385 arr.set(interface->nameStr(), make_tv<KindOfInt64>(1));
2388 ret.set(s_interfaces, VarNR(arr).tv());
2391 // traits
2393 Array arr = Array::Create();
2394 for (auto const& traitName : cls->preClass()->usedTraits()) {
2395 arr.set(StrNR(traitName), make_tv<KindOfInt64>(1));
2397 ret.set(s_traits, VarNR(arr).tv());
2400 // trait aliases
2402 ret.set(s_trait_aliases, VarNR(get_trait_alias_info(cls)).tv());
2405 // attributes
2407 if (cls->attrs() & AttrBuiltin) {
2408 ret.set(s_internal, true_varNR.tv());
2410 if (cls->attrs() & AttrFinal) {
2411 ret.set(s_final, true_varNR.tv());
2413 if (cls->attrs() & AttrAbstract) {
2414 ret.set(s_abstract, true_varNR.tv());
2416 if (cls->attrs() & AttrInterface) {
2417 ret.set(s_interface, true_varNR.tv());
2419 if (cls->attrs() & AttrTrait) {
2420 ret.set(s_trait, true_varNR.tv());
2422 ret.set(s_modifiers, make_tv<KindOfInt64>(
2423 get_modifiers(cls->attrs(), true, false))
2426 if (cls->getCtor()->attrs() & AttrPublic &&
2427 !(cls->attrs() & AttrAbstract) &&
2428 !(cls->attrs() & AttrInterface) &&
2429 !(cls->attrs() & AttrTrait)) {
2430 ret.set(s_instantiable, true_varNR.tv());
2434 // methods
2436 Array arr = Array::Create();
2438 // Fetch from PreClass as:
2439 // - the order is important
2440 // - we want type profiling info
2441 // and neither of these are in the Class...
2442 Func* const* methods = cls->preClass()->methods();
2443 size_t const numMethods = cls->preClass()->numMethods();
2444 for (Slot i = 0; i < numMethods; ++i) {
2445 const Func* m = methods[i];
2446 if (m->isGenerated()) continue;
2448 auto lowerName = HHVM_FN(strtolower)(m->nameStr());
2449 Array info = Array::Create();
2450 set_debugger_reflection_method_info(info, m, cls);
2451 arr.set(lowerName, VarNR(info).tv());
2454 for (Slot i = cls->traitsBeginIdx(); i < cls->traitsEndIdx(); ++i) {
2455 const Func* m = cls->getMethod(i);
2456 if (m->isGenerated()) continue;
2458 auto lowerName = HHVM_FN(strtolower)(m->nameStr());
2459 Array info = Array::Create();
2460 set_debugger_reflection_method_info(info, m, cls);
2461 arr.set(lowerName, VarNR(info).tv());
2463 ret.set(s_methods, VarNR(arr).tv());
2466 // properties
2468 auto arr = Array::Create();
2469 auto arrPriv = Array::Create();
2470 auto arrIdx = Array::Create();
2471 auto arrPrivIdx = Array::Create();
2473 auto const properties = cls->declProperties();
2474 auto const& propInitVec = cls->declPropInit();
2475 auto const nProps = cls->numDeclProperties();
2477 for (Slot i = 0; i < nProps; ++i) {
2478 auto const& prop = properties[i];
2479 auto const& default_val = tvAsCVarRef(&propInitVec[i]);
2480 auto info = Array::Create();
2481 if ((prop.attrs & AttrPrivate) == AttrPrivate) {
2482 if (prop.cls == cls) {
2483 set_instance_prop_info(info, &prop, default_val);
2484 arrPriv.set(StrNR(prop.name), VarNR(info).tv());
2485 arrPrivIdx.set(StrNR(prop.name), prop.serializationIdx);
2487 continue;
2489 set_instance_prop_info(info, &prop, default_val);
2490 arr.set(StrNR(prop.name), VarNR(info).tv());
2491 arrIdx.set(StrNR(prop.name), prop.serializationIdx);
2494 for (auto const& prop : cls->staticProperties()) {
2495 auto info = Array::Create();
2496 if ((prop.attrs & AttrPrivate) == AttrPrivate) {
2497 if (prop.cls == cls) {
2498 set_static_prop_info(info, &prop);
2499 arrPriv.set(StrNR(prop.name), VarNR(info).tv());
2500 arrPrivIdx.set(StrNR(prop.name), prop.serializationIdx);
2502 continue;
2504 set_static_prop_info(info, &prop);
2505 arr.set(StrNR(prop.name), VarNR(info).tv());
2506 arrIdx.set(StrNR(prop.name), prop.serializationIdx);
2508 ret.set(s_properties, VarNR(arr).tv());
2509 ret.set(s_private_properties, VarNR(arrPriv).tv());
2510 ret.set(s_properties_index, VarNR(arrIdx).tv());
2511 ret.set(s_private_properties_index, VarNR(arrPrivIdx).tv());
2514 // constants
2516 Array arr = Array::Create();
2518 size_t numConsts = cls->numConstants();
2519 const Class::Const* consts = cls->constants();
2521 for (size_t i = 0; i < numConsts; i++) {
2522 // Note: hphpc doesn't include inherited constants in
2523 // get_class_constants(), so mimic that behavior
2524 if (consts[i].cls == cls) {
2525 Cell value = cls->clsCnsGet(consts[i].name);
2526 assertx(value.m_type != KindOfUninit);
2527 arr.set(StrNR(consts[i].name), cellAsCVarRef(value));
2531 ret.set(s_constants, VarNR(arr).tv());
2534 { // source info
2535 const PreClass* pcls = cls->preClass();
2536 set_debugger_source_info(ret, pcls->unit()->filepath()->data(),
2537 pcls->line1(), pcls->line2());
2538 set_doc_comment(ret, pcls->docComment(), pcls->isBuiltin());
2541 // user attributes
2543 Array arr = Array::Create();
2544 const PreClass* pcls = cls->preClass();
2545 for (auto it = pcls->userAttributes().begin();
2546 it != pcls->userAttributes().end(); ++it) {
2547 arr.set(StrNR(it->first), tvAsCVarRef(&it->second));
2549 ret.set(s_attributes, VarNR(arr).tv());
2552 return ret;
2557 ///////////////////////////////////////////////////////////////////////////////