Don't return Variant& from Array functions
[hiphop-php.git] / hphp / runtime / ext / reflection / ext_reflection.cpp
blobef97478c7c1d79e85e46cf2d6122ef29c7d6375e
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/mixed-array.h"
23 #include "hphp/runtime/base/runtime-option.h"
24 #include "hphp/runtime/base/string-util.h"
25 #include "hphp/runtime/base/tv-refcount.h"
26 #include "hphp/runtime/base/type-structure.h"
27 #include "hphp/runtime/base/type-variant.h"
28 #include "hphp/runtime/vm/jit/translator-inline.h"
29 #include "hphp/runtime/vm/native-data.h"
31 #include "hphp/runtime/ext/debugger/ext_debugger.h"
32 #include "hphp/runtime/ext/std/ext_std_closure.h"
33 #include "hphp/runtime/ext/collections/ext_collections-set.h"
34 #include "hphp/runtime/ext/std/ext_std_misc.h"
35 #include "hphp/runtime/ext/string/ext_string.h"
36 #include "hphp/runtime/ext/extension-registry.h"
38 #include "hphp/parser/parser.h"
39 #include "hphp/system/systemlib.h"
41 #include "hphp/runtime/vm/native-prop-handler.h"
43 #include <functional>
44 #include <boost/algorithm/string/predicate.hpp>
46 namespace HPHP {
48 ///////////////////////////////////////////////////////////////////////////////
50 const StaticString
51 s_name("name"),
52 s___name("__name"),
53 s_version("version"),
54 s_info("info"),
55 s_ini("ini"),
56 s_constants("constants"),
57 s_constructor("constructor"),
58 s_functions("functions"),
59 s_classes("classes"),
60 s_access("access"),
61 s_public("public"),
62 s_protected("protected"),
63 s_private("private"),
64 s_file("file"),
65 s_line1("line1"),
66 s_line2("line2"),
67 s_doc("doc"),
68 s_modifiers("modifiers"),
69 s_class("class"),
70 s_prototype("prototype"),
71 s_ref("ref"),
72 s_index("index"),
73 s_type("type"),
74 s_nullable("nullable"),
75 s_msg("msg"),
76 s_is_optional("is_optional"),
77 s_is_variadic("is_variadic"),
78 s_default("default"),
79 s_defaultValue("defaultValue"),
80 s_defaultText("defaultText"),
81 s_params("params"),
82 s_final("final"),
83 s_abstract("abstract"),
84 s_instantiable("instantiable"),
85 s_internal("internal"),
86 s_is_async("is_async"),
87 s_is_closure("is_closure"),
88 s_is_generator("is_generator"),
89 s_hphp("hphp"),
90 s_static_variables("static_variables"),
91 s_extension("extension"),
92 s_interfaces("interfaces"),
93 s_traits("traits"),
94 s_interface("interface"),
95 s_trait("trait"),
96 s_methods("methods"),
97 s_properties("properties"),
98 s_private_properties("private_properties"),
99 s_properties_index("properties_index"),
100 s_private_properties_index("private_properties_index"),
101 s_attributes("attributes"),
102 s_function("function"),
103 s_trait_aliases("trait_aliases"),
104 s_varg("varg"),
105 s___invoke("__invoke"),
106 s_return_type("return_type"),
107 s_accessible("accessible"),
108 s_reflectionexception("ReflectionException"),
109 s_reflectionextension("ReflectionExtension"),
110 s_type_hint("type_hint"),
111 s_type_hint_builtin("type_hint_builtin"),
112 s_type_hint_nullable("type_hint_nullable");
114 Class* Reflection::s_ReflectionExceptionClass = nullptr;
115 Class* Reflection::s_ReflectionExtensionClass = nullptr;
117 Class* get_cls(const Variant& class_or_object) {
118 if (class_or_object.is(KindOfObject)) {
119 return class_or_object.toCObjRef()->getVMClass();
122 return Unit::loadClass(class_or_object.toString().get());
125 const Func* get_method_func(const Class* cls, const String& meth_name) {
126 const Func* func = cls->lookupMethod(meth_name.get());
127 if (!func) {
128 if (cls->attrs() & (AttrInterface | AttrAbstract | AttrTrait)) {
129 const Class::InterfaceMap& ifaces = cls->allInterfaces();
130 for (int i = 0, size = ifaces.size(); i < size; i++) {
131 func = ifaces[i]->lookupMethod(meth_name.get());
132 if (func) { break; }
136 assert(func == nullptr || func->isMethod());
137 return func;
140 Variant default_arg_from_php_code(const Func::ParamInfo& fpi,
141 const Func* func) {
142 assert(fpi.hasDefaultValue());
143 if (fpi.hasScalarDefaultValue()) {
144 // Most of the time the default value is scalar, so we can
145 // avoid evaling in the common case
146 return tvAsVariant((TypedValue*)&fpi.defaultValue);
147 } else {
148 // Eval PHP code to get default value. Note that access of
149 // undefined class constants can cause the eval() to
150 // fatal. Zend lets such fatals propagate, so don't bother catching
151 // exceptions here.
152 return g_context->getEvaledArg(
153 fpi.phpCode,
154 // We use cls() instead of implCls() because we want the namespace and
155 // class context for which the closure is scoped, not that of the Closure
156 // subclass (which, among other things, is always globally namespaced).
157 func->cls() ? func->cls()->nameStr() : func->nameStr(),
158 func->unit()
163 Array HHVM_FUNCTION(hphp_get_extension_info, const String& name) {
164 Array ret;
166 Extension *ext = ExtensionRegistry::get(name);
168 ret.set(s_name, name);
169 ret.set(s_version, ext ? ext->getVersion() : "");
170 ret.set(s_info, empty_string_variant_ref);
171 ret.set(s_ini, Array::Create());
172 ret.set(s_constants, Array::Create());
173 ret.set(s_functions, Array::Create());
174 ret.set(s_classes, Array::Create());
176 return ret;
179 int get_modifiers(Attr attrs, bool cls) {
180 int php_modifier = 0;
181 if (attrs & AttrAbstract) php_modifier |= cls ? 0x20 : 0x02;
182 if (attrs & AttrFinal) php_modifier |= cls ? 0x40 : 0x04;
183 if (attrs & AttrStatic) php_modifier |= 0x01;
184 if (!cls) { // AttrPublic bits are not valid on class (have other meaning)
185 if (attrs & AttrPublic) php_modifier |= 0x100;
186 if (attrs & AttrProtected) php_modifier |= 0x200;
187 if (attrs & AttrPrivate) php_modifier |= 0x400;
189 return php_modifier;
192 ALWAYS_INLINE
193 static Attr attrs_from_modifiers(int php_modifier, bool cls) {
194 Attr attrs = (Attr) 0;
195 if (php_modifier & (cls ? 0x20 : 0x02)) {
196 attrs = (Attr)(attrs | AttrAbstract);
198 if (php_modifier & (cls ? 0x40 : 0x04)) {
199 attrs = (Attr)(attrs | AttrFinal);
201 if (php_modifier & 0x01) { attrs = (Attr)(attrs | AttrStatic); }
202 if (!cls) { // AttrPublic bits are not valid on class (have other meaning)
203 if (php_modifier & 0x100) { attrs = (Attr)(attrs | AttrPublic); }
204 if (php_modifier & 0x200) { attrs = (Attr)(attrs | AttrProtected); }
205 if (php_modifier & 0x400) { attrs = (Attr)(attrs | AttrPrivate); }
207 return attrs;
210 static void set_attrs(Array& ret, int modifiers) {
211 if (modifiers & 0x100) {
212 ret.set(s_access, VarNR(s_public).tv());
213 ret.set(s_accessible, true_varNR.tv());
214 } else if (modifiers & 0x200) {
215 ret.set(s_access, VarNR(s_protected).tv());
216 ret.set(s_accessible, false_varNR.tv());
217 } else if (modifiers & 0x400) {
218 ret.set(s_access, VarNR(s_private).tv());
219 ret.set(s_accessible, false_varNR.tv());
220 } else {
221 assert(false);
223 ret.set(s_modifiers, make_tv<KindOfInt64>(modifiers));
224 if (modifiers & 0x1) {
225 ret.set(s_static, true_varNR.tv());
227 if (modifiers & 0x44) {
228 ret.set(s_final, true_varNR.tv());
230 if (modifiers & 0x22) {
231 ret.set(s_abstract, true_varNR.tv());
235 static void set_empty_doc_comment(Array& ret) {
236 ret.set(s_doc, false_varNR.tv());
239 static void set_doc_comment(Array& ret,
240 const StringData* comment,
241 bool isBuiltin) {
242 if (comment == nullptr || comment->empty()) {
243 set_empty_doc_comment(ret);
244 } else if (isBuiltin && !HHVM_FUNCTION(hphp_debugger_attached)) {
245 set_empty_doc_comment(ret);
246 } else {
247 assertx(!comment->isRefCounted());
248 ret.set(s_doc, make_tv<KindOfPersistentString>(comment));
252 static void set_instance_prop_info(Array& ret,
253 const Class::Prop* prop,
254 const Variant& default_val) {
255 ret.set(s_name, make_tv<KindOfPersistentString>(prop->name));
256 ret.set(s_default, true_varNR.tv());
257 ret.set(s_defaultValue, default_val);
258 set_attrs(ret, get_modifiers(prop->attrs, false) & ~0x66);
259 ret.set(s_class, make_tv<KindOfPersistentString>(prop->cls->name()));
260 set_doc_comment(ret, prop->docComment, prop->cls->isBuiltin());
262 if (prop->typeConstraint && prop->typeConstraint->size()) {
263 ret.set(s_type, make_tv<KindOfPersistentString>(prop->typeConstraint));
264 } else {
265 ret.set(s_type, false_varNR.tv());
269 static void set_dyn_prop_info(
270 Array &ret,
271 const Variant& name,
272 const StringData* className) {
273 ret.set(s_name, name);
274 set_attrs(ret, get_modifiers(AttrPublic, false) & ~0x66);
275 ret.set(s_class, make_tv<KindOfPersistentString>(className));
276 set_empty_doc_comment(ret);
277 ret.set(s_type, false_varNR.tv());
280 static void set_static_prop_info(Array &ret, const Class::SProp* prop) {
281 ret.set(s_name, make_tv<KindOfPersistentString>(prop->name));
282 ret.set(s_default, true_varNR.tv());
283 ret.set(s_defaultValue, prop->val);
284 set_attrs(ret, get_modifiers(prop->attrs, false) & ~0x66);
285 ret.set(s_class, make_tv<KindOfPersistentString>(prop->cls->name()));
286 set_doc_comment(ret, prop->docComment, prop->cls->isBuiltin());
287 if (prop->typeConstraint && prop->typeConstraint->size()) {
288 ret.set(s_type, make_tv<KindOfPersistentString>(prop->typeConstraint));
289 } else {
290 ret.set(s_type, false_varNR.tv());
294 static bool resolveConstant(const char *p, int64_t len, Variant &cns) {
295 // ltrim
296 while (len && (*p == ' ')) {
297 p++;
298 len--;
300 // rtrim
301 while (len && (p[len-1] == ' ')) {
302 len--;
305 String cname(p, len, CopyString);
307 if (!f_defined(cname)) {
308 cns = uninit_null();
309 return false;
312 cns = f_constant(cname);
313 return true;
316 bool resolveDefaultParameterConstant(const char *value, int64_t valueLen,
317 Variant &cns) {
318 const char *p = value;
319 const char *e = value + valueLen;
320 const char *s;
321 bool isLval = false;
322 int64_t lval = 0;
324 while ((s = strchr(p, '|'))) {
325 isLval = true;
326 if (!resolveConstant(p, s - p, cns)) {
327 return false;
329 lval |= cns.toInt64();
330 p = s + 1;
332 if (!resolveConstant(p, e - p, cns)) {
333 return false;
335 if (isLval) {
336 cns = cns.toInt64() | lval;
338 return true;
341 static bool isConstructor(const Func* func) {
342 PreClass* pcls = func->preClass();
343 if (!pcls || (pcls->attrs() & AttrInterface)) { return false; }
344 if (func->implCls()) { return func == func->implCls()->getCtor(); }
345 if (0 == strcasecmp("__construct", func->name()->data())) { return true; }
346 /* A same named function is not a constructor in a trait */
347 if (pcls->attrs() & AttrTrait) return false;
348 return pcls->name()->isame(func->name());
351 static const Class* get_prototype_class_from_interfaces(const Class *cls,
352 const Func *func) {
353 // only looks at the interfaces if the method is public
354 if (!func->isPublic()) return nullptr;
355 const Class::InterfaceMap& interfaces = cls->allInterfaces();
356 for (unsigned int i = 0, size = interfaces.size(); i < size; i++) {
357 const Class* iface = interfaces[i];
358 if (iface->preClass()->hasMethod(func->name())) return iface;
360 return nullptr;
363 Variant HHVM_FUNCTION(hphp_invoke, const String& name, const Variant& params) {
364 return invoke(name.data(), params);
367 static const StaticString s_invoke_not_instanceof_error(
368 "Given object is not an instance of the class this method was declared in"
371 static const StaticString s_invoke_non_object(
372 "Non-object passed to Invoke()"
375 Variant HHVM_FUNCTION(hphp_invoke_method, const Variant& obj,
376 const String& cls,
377 const String& name,
378 const Variant& params) {
379 if (obj.isNull()) {
380 return invoke_static_method(cls, name, params);
383 if (!obj.is(KindOfObject)) {
384 Reflection::ThrowReflectionExceptionObject(s_invoke_non_object);
387 auto const providedClass = Unit::loadClass(cls.get());
388 if (!providedClass) {
389 raise_error("Call to undefined method %s::%s()", cls.data(), name.data());
391 auto const selectedFunc = providedClass->lookupMethod(name.get());
392 if (!selectedFunc) {
393 raise_error("Call to undefined method %s::%s()", cls.data(), name.data());
396 auto const objData = obj.toCObjRef().get();
397 auto const implementingClass = selectedFunc->implCls();
398 if (!objData->instanceof(implementingClass)) {
399 Reflection::ThrowReflectionExceptionObject(s_invoke_not_instanceof_error);
402 // Get the CallCtx this way instead of using vm_decode_function() because
403 // vm_decode_function() has no way to specify a class independent from the
404 // class::function being called.
405 // Note that this breaks the rules for name lookup (for protected and private)
406 // but that's okay because so does Zend's implementation.
407 CallCtx ctx;
408 ctx.cls = providedClass;
409 ctx.this_ = objData;
410 ctx.invName = nullptr;
411 ctx.func = selectedFunc;
413 return Variant::attach(
414 g_context->invokeFunc(ctx, params)
418 Object HHVM_FUNCTION(hphp_create_object, const String& name,
419 const Variant& params) {
420 return Object::attach(g_context->createObject(name.get(), params));
423 Object HHVM_FUNCTION(hphp_create_object_without_constructor,
424 const String& name) {
425 return Object::attach(
426 g_context->createObject(name.get(), init_null_variant, false)
430 Variant HHVM_FUNCTION(hphp_get_property, const Object& obj, const String& cls,
431 const String& prop) {
432 /* It's possible to get a ReflectionProperty for a property which
433 * no longer exists. Silentyly fail to match PHP5 behavior
435 return obj->o_get(prop, false /* error */, cls);
438 void HHVM_FUNCTION(hphp_set_property, const Object& obj, const String& cls,
439 const String& prop, const Variant& value) {
440 if (!cls.empty() && RuntimeOption::EvalAuthoritativeMode) {
441 raise_error(
442 "We've already made many assumptions about private variables. "
443 "You can't change accessibility in Whole Program mode"
448 * This interface can be used to clear memo caches, but we don't want to allow
449 * setting the memo cache values to arbitrary values. HHBBC and the JIT make
450 * aggressive assumptions about the layout and values of the caches to improve
451 * performance. So, if the property being set is a memo cache, we'll allow
452 * resetting it to an empty value, but nothing else.
454 * Furthermore, existing PHP code assumes that the memo cache stores arrays,
455 * not dicts, so for backwards compatibility, we'll allow null or any empty
456 * array-like value to mean clear.
458 auto const isMultiMemo = prop.slice().endsWith("$multi$memoize_cache");
459 auto const isSingleMemo = prop.slice().endsWith("$single$memoize_cache");
460 auto const isGuardedSingleMemo =
461 prop.slice().endsWith("$guarded_single$memoize_cache");
462 auto const isGuard =
463 prop.slice().endsWith("$guarded_single$memoize_cache$guard");
465 if (isGuard) {
466 // Allow setting the guard to false only. Clear out the associated cache at
467 // the same time.
468 auto const tv = value.asTypedValue();
469 if (tv->m_type != KindOfBoolean || tv->m_data.num) {
470 raise_error("Reflection can only reset memoize caches");
472 obj->o_set(prop, false_varNR, cls);
473 obj->o_set(
474 prop.slice().subpiece(0, prop.length() - 6),
475 init_null_variant,
478 return;
481 if (isMultiMemo || isSingleMemo || isGuardedSingleMemo) {
482 auto const tv = value.asTypedValue();
483 if (tv->m_type != KindOfNull &&
484 (!isArrayLikeType(tv->m_type) || tv->m_data.parr->size() != 0)) {
485 raise_error("Reflection can only reset memoize caches");
487 // Reset to empty value. Depending on the type of the memo cache, this might
488 // be null or an empty dict.
489 if (isMultiMemo) {
490 obj->o_set(
491 prop,
492 cellAsCVarRef(make_tv<KindOfPersistentDict>(staticEmptyDictArray())),
495 } else if (isSingleMemo || isGuardedSingleMemo) {
496 obj->o_set(prop, init_null_variant, cls);
497 if (isGuardedSingleMemo) {
498 // If there's a guard, reset it to false as well.
499 obj->o_set(folly::sformat("{}$guard", prop), false_varNR, cls);
502 return;
505 obj->o_set(prop, value, cls);
508 Variant HHVM_FUNCTION(hphp_get_static_property, const String& cls,
509 const String& prop,
510 bool force) {
511 auto const sd = cls.get();
512 auto const class_ = Unit::lookupClass(sd);
513 if (!class_) {
514 raise_error("Non-existent class %s", sd->data());
516 VMRegAnchor _;
518 auto const lookup = class_->getSProp(
519 force ? class_ : arGetContextClass(vmfp()),
520 prop.get()
522 if (!lookup.prop) {
523 raise_error("Class %s does not have a property named %s",
524 sd->data(), prop.get()->data());
526 if (!lookup.accessible) {
527 raise_error("Invalid access to class %s's property %s",
528 sd->data(), prop.get()->data());
530 return tvAsVariant(lookup.prop);
533 void HHVM_FUNCTION(hphp_set_static_property, const String& cls,
534 const String& prop,
535 const Variant& value,
536 bool force) {
537 if (RuntimeOption::EvalAuthoritativeMode) {
538 raise_error("Setting static properties through reflection is not "
539 "allowed in RepoAuthoritative mode");
541 auto const sd = cls.get();
542 auto const class_ = Unit::lookupClass(sd);
544 if (!class_) raise_error("Non-existent class %s", sd->data());
546 VMRegAnchor _;
548 auto const lookup = class_->getSProp(
549 force ? class_ : arGetContextClass(vmfp()),
550 prop.get()
552 if (!lookup.prop) {
553 raise_error("Class %s does not have a property named %s",
554 cls.get()->data(), prop.get()->data());
556 if (!lookup.accessible) {
557 raise_error("Invalid access to class %s's property %s",
558 sd->data(), prop.get()->data());
562 * Like in hphp_set_property(), this interface can be used to clear memo
563 * caches, but we don't want to allow setting the memo cache values to
564 * arbitrary values. HHBBC and the JIT make aggressive assumptions about the
565 * layout and values of the caches to improve performance. So, if the property
566 * being set is a memo cache, we'll allow resetting it to an empty value, but
567 * nothing else.
569 * Furthermore, existing PHP code assumes that the memo cache stores arrays,
570 * not dicts, so for backwards compatibility, we'll allow null or any empty
571 * array-like value to mean clear.
573 auto const isMultiMemo = prop.slice().endsWith("$multi$memoize_cache");
574 auto const isSingleMemo = prop.slice().endsWith("$single$memoize_cache");
575 auto const isGuardedSingleMemo =
576 prop.slice().endsWith("$guarded_single$memoize_cache");
577 auto const isGuard =
578 prop.slice().endsWith("$guarded_single$memoize_cache$guard");
580 if (isGuard) {
581 // Allow setting the guard to false only. Clear out the associated cache at
582 // the same time.
583 auto const tv = value.asTypedValue();
584 if (tv->m_type != KindOfBoolean || tv->m_data.num) {
585 raise_error("Reflection can only reset memoize caches");
587 cellSet(make_tv<KindOfBoolean>(false), *lookup.prop);
589 auto const cacheLookup = class_->getSProp(
590 force ? class_ : arGetContextClass(vmfp()),
591 String{prop.slice().subpiece(0, prop.length() - 6)}.get()
593 if (cacheLookup.prop) {
594 cellSet(make_tv<KindOfNull>(), *cacheLookup.prop);
596 return;
599 if (isMultiMemo || isSingleMemo || isGuardedSingleMemo) {
600 auto const tv = value.asTypedValue();
601 if (tv->m_type != KindOfNull &&
602 (!isArrayLikeType(tv->m_type) || tv->m_data.parr->size() != 0)) {
603 raise_error("Reflection can only reset memoize caches");
605 // Reset to empty value. Depending on the type of the memo cache, this might
606 // be null or an empty dict.
607 if (isMultiMemo) {
608 cellSet(
609 make_tv<KindOfPersistentDict>(staticEmptyDictArray()),
610 *lookup.prop
612 } else if (isSingleMemo || isGuardedSingleMemo) {
613 cellSet(make_tv<KindOfNull>(), *lookup.prop);
614 if (isGuardedSingleMemo) {
615 // If there's a guard, reset it to false as well.
616 auto const guardLookup = class_->getSProp(
617 force ? class_ : arGetContextClass(vmfp()),
618 String{folly::sformat("{}$guard", prop)}.get()
620 if (guardLookup.prop) {
621 cellSet(make_tv<KindOfBoolean>(false), *guardLookup.prop);
625 return;
628 tvAsVariant(lookup.prop) = value;
632 * cls_or_obj: the name of a class or an instance of the class;
633 * cns_name: the name of the type constant of the class;
635 * If the type constant exists and is not abstract, this function
636 * returns the shape representing the type associated with the type
637 * constant.
639 Array HHVM_FUNCTION(type_structure,
640 const Variant& cls_or_obj, const Variant& cns_name) {
641 auto const cns_sd = cns_name.getStringDataOrNull();
642 if (!cns_sd) {
643 auto name = cls_or_obj.toString();
645 auto const typeAlias = Unit::loadTypeAlias(name.get());
647 if (!typeAlias) {
648 raise_error("Non-existent type alias %s", name.get()->data());
651 auto const typeStructure = typeAlias->typeStructure;
652 assertx(!typeStructure.empty());
653 assertx(typeStructure.isDArray());
654 Array resolved;
655 try {
656 bool persistent = true;
657 resolved = TypeStructure::resolve(name, typeStructure, persistent);
658 } catch (Exception& e) {
659 raise_error("resolving type alias %s failed. "
660 "Have you declared all classes in the type alias",
661 name.get()->data());
663 assertx(!resolved.empty());
664 assertx(resolved.isDArray());
665 return resolved;
668 auto const cls = get_cls(cls_or_obj);
670 if (!cls) {
671 raise_error("Non-existent class %s", cls_or_obj.toString().get()->data());
674 auto const cls_sd = cls->name();
675 auto typeCns = cls->clsCnsGet(cns_sd, true);
676 if (typeCns.m_type == KindOfUninit) {
677 if (cls->hasTypeConstant(cns_sd, true)) {
678 raise_error("Type constant %s::%s is abstract",
679 cls_sd->data(), cns_sd->data());
680 } else {
681 raise_error("Non-existent type constant %s::%s",
682 cls_sd->data(), cns_sd->data());
686 assertx(isArrayType(typeCns.m_type));
687 assertx(typeCns.m_data.parr->isDArray());
688 assertx(typeCns.m_data.parr->isStatic());
689 return Array::attach(typeCns.m_data.parr);
692 String HHVM_FUNCTION(hphp_get_original_class_name, const String& name) {
693 Class* cls = Unit::loadClass(name.get());
694 if (!cls) return empty_string();
695 return cls->nameStr();
698 [[noreturn]]
699 void Reflection::ThrowReflectionExceptionObject(const Variant& message) {
700 Object inst{s_ReflectionExceptionClass};
701 tvDecRefGen(
702 g_context->invokeFunc(s_ReflectionExceptionClass->getCtor(),
703 make_packed_array(message),
704 inst.get())
706 throw_object(inst);
710 /////////////////////////////////////////////////////////////////////////////
711 // class ReflectionFuncHandle
713 const StaticString s_ReflectionFuncHandle("ReflectionFuncHandle");
715 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getFileName) {
716 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
717 if (func->isBuiltin()) {
718 return false;
720 auto file = func->unit()->filepath()->data();
721 if (!file) { file = ""; }
722 if (file[0] != '/') {
723 return String(RuntimeOption::SourceRoot + file);
724 } else {
725 return String(file);
729 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getStartLine) {
730 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
731 if (func->isBuiltin()) {
732 return false;
734 return func->line1();
737 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getEndLine) {
738 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
739 if (func->isBuiltin()) {
740 return false;
742 return func->line2();
745 static Variant HHVM_METHOD(ReflectionFunctionAbstract, getDocComment) {
746 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
747 auto const comment = func->docComment();
748 if (comment == nullptr || comment->empty()) {
749 return false_varNR;
750 } else if (func->isBuiltin() && !HHVM_FUNCTION(hphp_debugger_attached)) {
751 return false_varNR;
752 } else {
753 auto ret = const_cast<StringData*>(comment);
754 return VarNR(ret);
758 ALWAYS_INLINE
759 static Array get_function_static_variables(const Func* func) {
760 auto const& staticVars = func->staticVars();
762 auto size = staticVars.size();
763 ArrayInit ai(size, ArrayInit::Mixed{});
765 for (size_t i = 0; i < staticVars.size(); ++i) {
766 const Func::SVInfo &sv = staticVars[i];
767 auto const staticLocalData = rds::bindStaticLocal(func, sv.name);
768 // FIXME: this should not require variant hops
769 ai.setUnknownKey(
770 VarNR(sv.name),
771 staticLocalData.isInit()
772 ? tvAsCVarRef(staticLocalData.get()->ref.tv())
773 : uninit_variant
776 return ai.toArray();
779 static Array HHVM_METHOD(ReflectionFunctionAbstract, getStaticVariables) {
780 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
781 return get_function_static_variables(func);
784 static String HHVM_METHOD(ReflectionFunctionAbstract, getName) {
785 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
786 auto ret = const_cast<StringData*>(func->name());
787 return String(ret);
790 static bool HHVM_METHOD(ReflectionFunctionAbstract, isHack) {
791 if (RuntimeOption::EnableHipHopSyntax) {
792 return true;
794 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
795 return func->unit()->isHHFile();
798 static bool HHVM_METHOD(ReflectionFunctionAbstract, isInternal) {
799 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
800 return func->isBuiltin();
803 static bool HHVM_METHOD(ReflectionFunctionAbstract, isGenerator) {
804 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
805 return func->isGenerator();
808 static bool HHVM_METHOD(ReflectionFunctionAbstract, isAsync) {
809 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
810 return func->isAsync();
813 static bool HHVM_METHOD(ReflectionFunctionAbstract, isVariadic) {
814 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
815 return func->hasVariadicCaptureParam();
818 static bool HHVM_METHOD(ReflectionFunctionAbstract, returnsReference) {
819 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
820 return func->attrs() & AttrReference;
823 static int64_t HHVM_METHOD(ReflectionFunctionAbstract, getNumberOfParameters) {
824 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
825 return func->numParams();
828 // If we are in <?php and in PHP 7 mode w.r.t. scalar types
829 ALWAYS_INLINE static bool isPhpTypeHintEnabled(const Func* func) {
830 return (!(func->unit()->isHHFile() || RuntimeOption::EnableHipHopSyntax) &&
831 RuntimeOption::PHP7_ScalarTypes
835 ALWAYS_INLINE
836 static Array get_function_param_info(const Func* func) {
837 const Func::ParamInfoVec& params = func->params();
838 PackedArrayInit ai(func->numParams());
840 for (int i = 0; i < func->numParams(); ++i) {
841 Array param = Array::Create();
842 const Func::ParamInfo& fpi = params[i];
844 param.set(s_index, make_tv<KindOfInt64>(i));
845 param.set(s_name, make_tv<KindOfPersistentString>(func->localNames()[i]));
847 auto const nonExtendedConstraint =
848 fpi.typeConstraint.hasConstraint() &&
849 !fpi.typeConstraint.isExtended();
850 auto const type = nonExtendedConstraint
851 ? fpi.typeConstraint.typeName()
852 : staticEmptyString();
854 param.set(s_type, make_tv<KindOfPersistentString>(type));
855 const StringData* typeHint = fpi.userType
856 ? fpi.userType
857 : staticEmptyString();
858 param.set(s_type_hint, make_tv<KindOfPersistentString>(typeHint));
860 std::string phpTypeHint(isPhpTypeHintEnabled(func) ? typeHint->toCppString() : "");
862 if (!phpTypeHint.empty() && phpTypeHint[0] == '?') {
863 phpTypeHint = phpTypeHint.substr(1);
864 param.set(s_type_hint, phpTypeHint);
867 // callable typehint considered builtin; stdclass typehint is not
868 if (
869 fpi.typeConstraint.isCallable() ||
870 (fpi.typeConstraint.underlyingDataType() &&
871 fpi.typeConstraint.underlyingDataType() != KindOfObject
874 param.set(s_type_hint_builtin, true_varNR.tv());
875 // If we are in <?php and in PHP 7 mode w.r.t. scalar types, then we want
876 // the types to come back as PHP 7 style scalar types, not HH\ style
877 // scalar types.
878 if (!phpTypeHint.empty() && boost::starts_with(phpTypeHint, "HH\\")) {
879 phpTypeHint = phpTypeHint.substr(3);
880 param.set(s_type_hint, phpTypeHint);
882 } else {
883 param.set(s_type_hint_builtin, false_varNR.tv());
885 param.set(s_function, make_tv<KindOfPersistentString>(func->name()));
886 if (func->preClass()) {
887 param.set(
888 s_class,
889 make_tv<KindOfPersistentString>(
890 func->implCls() ? func->implCls()->name()
891 : func->preClass()->name()
895 if (!nonExtendedConstraint || fpi.typeConstraint.isNullable()) {
896 param.set(s_nullable, true_varNR.tv());
897 param.set(s_type_hint_nullable, true_varNR.tv());
898 } else {
899 param.set(s_type_hint_nullable, false_varNR.tv());
902 if (fpi.phpCode) {
903 Variant v = default_arg_from_php_code(fpi, func);
904 param.set(s_default, v);
905 param.set(s_defaultText, make_tv<KindOfPersistentString>(fpi.phpCode));
908 if (func->byRef(i)) {
909 param.set(s_ref, true_varNR.tv());
911 if (fpi.isVariadic()) {
912 param.set(s_is_variadic, true_varNR.tv());
915 Array userAttrs = Array::Create();
916 for (auto it = fpi.userAttributes.begin();
917 it != fpi.userAttributes.end(); ++it) {
918 userAttrs.set(StrNR(it->first), it->second);
920 param.set(s_attributes, VarNR(userAttrs).tv());
922 ai.append(VarNR(param).tv());
925 auto arr = ai.toArray();
927 bool isOptional = true;
928 for (int i = func->numParams() - 1; i >= 0; i--) {
929 auto& param = asArrRef(arr.lvalAt(i));
931 isOptional = isOptional && (param.exists(s_default) ||
932 param.exists(s_is_variadic));
933 param.set(s_is_optional, isOptional);
935 return arr;
938 // helper for getParameters
939 static Array HHVM_METHOD(ReflectionFunctionAbstract, getParamInfo) {
940 // FIXME: each parameter info should be HNI with a handle to the
941 // Func::ParamInfo
942 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
943 return get_function_param_info(func);
946 // helper for getReturnTypeText
947 static String HHVM_METHOD(ReflectionFunctionAbstract, getReturnTypeHint) {
948 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
949 auto retTypeSD = func->returnUserType();
950 if (retTypeSD && retTypeSD->size()) {
951 auto ret = const_cast<StringData*>(retTypeSD);
952 return String(ret);
954 return String();
957 static Array HHVM_METHOD(ReflectionFunctionAbstract, getRetTypeInfo) {
958 Array retTypeInfo = Array::Create();
959 auto name = HHVM_MN(ReflectionFunctionAbstract, getReturnTypeHint)(this_);
960 if (name && !name.empty()) {
961 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
962 auto retType = func->returnTypeConstraint();
963 if (retType.isNullable()) {
964 retTypeInfo.set(s_type_hint_nullable, true_varNR.tv());
965 if (isPhpTypeHintEnabled(func)) {
966 name = name.substr(1); //removes '?' - e.g. ?int -> int
968 } else {
969 retTypeInfo.set(s_type_hint_nullable, false_varNR.tv());
972 if (
973 retType.isCallable() || // callable type hint is considered builtin
974 (retType.underlyingDataType() &&
975 retType.underlyingDataType() != KindOfObject
978 retTypeInfo.set(s_type_hint_builtin, true_varNR.tv());
979 // If we are in <?php and in PHP 7 mode w.r.t. scalar types, then we want
980 // the types to come back as PHP 7 style scalar types, not HH\ style
981 // scalar types.
982 if (isPhpTypeHintEnabled(func) && boost::starts_with(name.toCppString(), "HH\\")) {
983 name = name.substr(3);
985 } else {
986 retTypeInfo.set(s_type_hint_builtin, false_varNR.tv());
988 } else {
989 name = staticEmptyString();
990 retTypeInfo.set(s_type_hint_nullable, false_varNR.tv());
991 retTypeInfo.set(s_type_hint_builtin, false_varNR.tv());
993 retTypeInfo.set(s_type_hint, name);
994 return retTypeInfo;
997 ALWAYS_INLINE
998 static Array get_function_user_attributes(const Func* func) {
999 auto userAttrs = func->userAttributes();
1001 ArrayInit ai(userAttrs.size(), ArrayInit::Mixed{});
1002 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
1003 ai.set(VarNR::MakeKey(StrNR(it->first).asString()).tv(), it->second);
1005 return ai.toArray();
1008 static Array HHVM_METHOD(ReflectionFunctionAbstract, getAttributes) {
1009 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1010 return get_function_user_attributes(func);
1013 // ------------------------- class ReflectionMethod
1015 // helper for __construct
1016 static bool HHVM_METHOD(ReflectionMethod, __init,
1017 const Variant& cls_or_object, const String& meth_name) {
1018 auto const cls = get_cls(cls_or_object);
1019 if (!cls || meth_name.isNull()) {
1020 // caller raises exception
1021 return false;
1023 auto const func = get_method_func(cls, meth_name);
1024 if (!func) {
1025 // caller raises exception
1026 return false;
1028 assert(func->isMethod());
1029 ReflectionFuncHandle::Get(this_)->setFunc(func);
1030 return true;
1033 static bool HHVM_METHOD(ReflectionMethod, isFinal) {
1034 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1035 return func->attrs() & AttrFinal;
1038 static bool HHVM_METHOD(ReflectionMethod, isAbstract) {
1039 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1040 return func->attrs() & AttrAbstract;
1043 static bool HHVM_METHOD(ReflectionMethod, isPublic) {
1044 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1045 return func->attrs() & AttrPublic;
1048 static bool HHVM_METHOD(ReflectionMethod, isProtected) {
1049 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1050 return func->attrs() & AttrProtected;
1053 static bool HHVM_METHOD(ReflectionMethod, isPrivate) {
1054 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1055 return func->attrs() & AttrPrivate;
1058 static bool HHVM_METHOD(ReflectionMethod, isStatic) {
1059 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1060 return func->attrs() & AttrStatic;
1063 static bool HHVM_METHOD(ReflectionMethod, isConstructor) {
1064 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1065 return isConstructor(func);
1068 static int HHVM_METHOD(ReflectionMethod, getModifiers) {
1069 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1070 return get_modifiers(func->attrs(), false);
1073 // private helper for getPrototype
1074 static String HHVM_METHOD(ReflectionMethod, getPrototypeClassname) {
1075 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1076 const Class *prototypeCls = nullptr;
1077 if (func->baseCls() != nullptr && func->baseCls() != func->implCls()) {
1078 prototypeCls = func->baseCls();
1079 const Class *result = get_prototype_class_from_interfaces(
1080 prototypeCls, func);
1081 if (result) { prototypeCls = result; }
1082 } else if (func->isMethod()) {
1083 // lookup the prototype in the interfaces
1084 prototypeCls = get_prototype_class_from_interfaces(func->implCls(), func);
1086 if (prototypeCls) {
1087 auto ret = const_cast<StringData*>(prototypeCls->name());
1088 return String(ret);
1090 return String();
1093 // private helper for getDeclaringClass
1094 static String HHVM_METHOD(ReflectionMethod, getDeclaringClassname) {
1095 auto const func = ReflectionFuncHandle::GetFuncFor(this_);
1096 auto ret = const_cast<StringData*>(func->implCls()->name());
1097 return String(ret);
1100 // ------------------------- class ReflectionFunction
1102 // helper for __construct
1103 static bool HHVM_METHOD(ReflectionFunction, __initName, const String& name) {
1104 if (name.isNull()) { return false; }
1105 const Func* func = Unit::loadFunc(name.get());
1106 if (!func) { return false; }
1107 ReflectionFuncHandle::Get(this_)->setFunc(func);
1108 return true;
1111 // helper for __construct
1112 static bool HHVM_METHOD(ReflectionFunction, __initClosure,
1113 const Object& closure) {
1114 auto const cls = get_cls(closure);
1115 assert(cls);
1116 if (!cls) { return false; }
1117 const Func* func = cls->lookupMethod(s___invoke.get());
1118 if (!func) {
1119 // caller raises exception
1120 return false;
1122 assert(func->isClosureBody());
1123 assert(func->implCls()->isScopedClosure());
1124 ReflectionFuncHandle::Get(this_)->setFunc(func);
1125 return true;
1128 const StaticString s_ExpectedClosureInstance("Expected closure instance");
1130 // helper for getClosureScopeClass
1131 static Variant HHVM_METHOD(ReflectionFunction, getClosureScopeClassname,
1132 const Object& closure) {
1133 if (!closure->instanceof(c_Closure::classof())) {
1134 SystemLib::throwExceptionObject(s_ExpectedClosureInstance);
1136 if (auto scope = c_Closure::fromObject(closure.get())->getScope()) {
1137 return String(const_cast<StringData*>(scope->name()));
1139 return init_null_variant;
1142 static Variant HHVM_METHOD(ReflectionFunction, getClosureThisObject,
1143 const Object& closure) {
1144 if (!closure->instanceof(c_Closure::classof())) {
1145 SystemLib::throwExceptionObject(s_ExpectedClosureInstance);
1147 auto const clos = c_Closure::fromObject(closure.get());
1148 if (clos->hasThis()) {
1149 return Object{clos->getThis()};
1151 return init_null_variant;
1154 // helper for getStaticVariables
1155 static Array HHVM_METHOD(ReflectionFunction, getClosureUseVariables,
1156 const Object& closure) {
1157 auto const cls = get_cls(closure);
1158 assertx(cls);
1159 MixedArrayInit ai(cls->numDeclProperties());
1160 auto propVal = closure->propVec();
1161 for (auto const& prop : cls->declProperties()) {
1162 // Closure static locals are represented as special instance properties
1163 // with a mangled name.
1164 if (prop.name->data()[0] == '8') {
1165 static const char prefix[] = "86static_";
1166 assert(0 == strncmp(prop.name->data(), prefix, sizeof prefix - 1));
1167 String strippedName(prop.name->data() + sizeof prefix - 1,
1168 prop.name->size() - sizeof prefix + 1,
1169 CopyString);
1170 ai.setUnknownKey(VarNR(strippedName), tvAsCVarRef(propVal));
1171 } else {
1172 ai.setWithRef(StrNR(prop.name), *propVal);
1174 propVal++;
1176 return ai.toArray();
1179 /////////////////////////////////////////////////////////////////////////////
1180 // class ReflectionClass
1182 const StaticString s_ReflectionClassHandle("ReflectionClassHandle");
1184 // helper for __construct
1185 static String HHVM_METHOD(ReflectionClass, __init, const String& name) {
1186 return ReflectionClassHandle::Get(this_)->init(name);
1189 static String HHVM_METHOD(ReflectionClass, getName) {
1190 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1191 return cls->nameStr();
1194 static String HHVM_METHOD(ReflectionClass, getParentName) {
1195 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1196 return cls->parentStr();
1199 static bool HHVM_METHOD(ReflectionClass, isHack) {
1200 if (RuntimeOption::EnableHipHopSyntax) {
1201 return true;
1203 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1204 return cls->preClass()->unit()->isHHFile();
1207 static bool HHVM_METHOD(ReflectionClass, isInternal) {
1208 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1209 return cls->attrs() & AttrBuiltin;
1212 static bool HHVM_METHOD(ReflectionClass, isInstantiable) {
1213 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1214 return !(cls->attrs() & (AttrAbstract | AttrInterface | AttrTrait | AttrEnum))
1215 && (cls->getCtor()->attrs() & AttrPublic);
1218 static bool HHVM_METHOD(ReflectionClass, isFinal) {
1219 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1220 return cls->attrs() & AttrFinal;
1223 static bool HHVM_METHOD(ReflectionClass, isAbstract) {
1224 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1225 return cls->attrs() & AttrAbstract;
1228 static bool HHVM_METHOD(ReflectionClass, isInterface) {
1229 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1230 return cls->attrs() & AttrInterface;
1233 static bool HHVM_METHOD(ReflectionClass, isTrait) {
1234 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1235 return cls->attrs() & AttrTrait;
1238 static bool HHVM_METHOD(ReflectionClass, isEnum) {
1239 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1240 return cls->attrs() & AttrEnum;
1243 static int HHVM_METHOD(ReflectionClass, getModifiers) {
1244 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1245 return get_modifiers(cls->attrs(), true);
1248 static Variant HHVM_METHOD(ReflectionClass, getFileName) {
1249 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1250 if (cls->attrs() & AttrBuiltin) {
1251 return false_varNR;
1253 auto file = cls->preClass()->unit()->filepath()->data();
1254 if (!file) { file = ""; }
1255 if (file[0] != '/') {
1256 return String(RuntimeOption::SourceRoot + file);
1257 } else {
1258 return String(file);
1262 static Variant HHVM_METHOD(ReflectionClass, getStartLine) {
1263 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1264 if (cls->isBuiltin()) {
1265 return false;
1267 return cls->preClass()->line1();
1270 static Variant HHVM_METHOD(ReflectionClass, getEndLine) {
1271 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1272 if (cls->isBuiltin()) {
1273 return false;
1275 return cls->preClass()->line2();
1278 static Variant HHVM_METHOD(ReflectionClass, getDocComment) {
1279 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1280 auto const pcls = cls->preClass();
1281 auto const comment = pcls->docComment();
1282 if (comment == nullptr || comment->empty()) {
1283 return false_varNR;
1284 } else if (pcls->isBuiltin() && !HHVM_FUNCTION(hphp_debugger_attached)) {
1285 return false_varNR;
1286 } else {
1287 auto ret = const_cast<StringData*>(comment);
1288 return VarNR(ret);
1292 static Array HHVM_METHOD(ReflectionClass, getRequirementNames) {
1293 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1294 if (!(cls->attrs() & (AttrTrait | AttrInterface))) {
1295 // requirements are applied to abstract/concrete classes when they use
1296 // a trait / implement an interface
1297 return empty_array();
1300 auto const& requirements = cls->allRequirements();
1301 auto numReqs = requirements.size();
1302 if (numReqs == 0) {
1303 return empty_array();
1306 PackedArrayInit pai(numReqs);
1307 for (int i = 0; i < numReqs; ++i) {
1308 auto const& req = requirements[i];
1309 pai.append(Variant{const_cast<StringData*>(req->name())});
1311 return pai.toArray();
1314 static Array HHVM_METHOD(ReflectionClass, getInterfaceNames) {
1315 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1317 auto st = req::make<c_Set>();
1318 auto const& allIfaces = cls->allInterfaces();
1319 st->reserve(allIfaces.size());
1321 for (auto const& interface: cls->declInterfaces()) {
1322 st->add(const_cast<StringData*>(interface->name()));
1324 if (allIfaces.size() > cls->declInterfaces().size()) {
1325 for (int i = 0; i < allIfaces.size(); ++i) {
1326 auto const& interface = allIfaces[i];
1327 st->add(const_cast<StringData*>(interface->name()));
1331 PackedArrayInit ai(st->size());
1332 for (ArrayIter iter(st.get()); iter; ++iter) {
1333 ai.append(iter.secondValPlus());
1335 return ai.toArray();
1338 static Array HHVM_METHOD(ReflectionClass, getTraitNames) {
1339 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1340 auto const& traits = cls->preClass()->usedTraits();
1341 PackedArrayInit ai(traits.size());
1342 for (const StringData* traitName : traits) {
1343 ai.append(Variant{const_cast<StringData*>(traitName)});
1345 return ai.toArray();
1348 static Array get_trait_alias_info(const Class* cls) {
1349 auto const& aliases = cls->traitAliases();
1351 if (aliases.size()) {
1352 ArrayInit ai(aliases.size(), ArrayInit::Map{});
1354 for (auto const& namePair : aliases) {
1355 ai.set(StrNR(namePair.first), VarNR(namePair.second).tv());
1357 return ai.toArray();
1358 } else {
1359 // Even if we have alias rules, if we're in repo mode, they will be applied
1360 // during the trait flattening step, and we won't populate traitAliases()
1361 // on the Class.
1362 auto const& rules = cls->preClass()->traitAliasRules();
1364 ArrayInit ai(rules.size(), ArrayInit::Map{});
1366 for (auto const& rule : rules) {
1367 auto namePair = rule.asNamePair();
1368 ai.set(StrNR(namePair.first), VarNR(namePair.second).tv());
1370 return ai.toArray();
1374 static Array HHVM_METHOD(ReflectionClass, getTraitAliases) {
1375 return get_trait_alias_info(ReflectionClassHandle::GetClassFor(this_));
1378 static bool HHVM_METHOD(ReflectionClass, hasMethod, const String& name) {
1379 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1380 return (get_method_func(cls, name) != nullptr);
1383 // helper for getMethods: returns a Set
1384 static Object HHVM_METHOD(ReflectionClass, getMethodOrder, int64_t filter) {
1385 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1386 Attr mask = attrs_from_modifiers(filter, false);
1388 // At each step, we fetch from the PreClass is important because the
1389 // order in which getMethods returns matters
1390 req::StringISet visitedMethods;
1391 auto st = req::make<c_Set>();
1392 st->reserve(cls->numMethods());
1394 auto add = [&] (const Func* m) {
1395 if (m->isGenerated()) return;
1396 if (visitedMethods.count(m->nameStr())) return;
1398 visitedMethods.insert(m->nameStr());
1399 if (m->attrs() & mask) {
1400 st->add(HHVM_FN(strtolower)(m->nameStr()).get());
1404 std::function<void(const Class*)> collect;
1405 std::function<void(const Class*)> collectInterface;
1407 collect = [&] (const Class* clas) {
1408 if (!clas) return;
1410 auto const methods = clas->preClass()->methods();
1411 auto const numMethods = clas->preClass()->numMethods();
1413 auto numDeclMethods = clas->preClass()->numDeclMethods();
1414 if (numDeclMethods == -1) numDeclMethods = numMethods;
1416 // Add declared methods.
1417 for (Slot i = 0; i < numDeclMethods; ++i) {
1418 add(methods[i]);
1421 // Recurse; we need to order the parent's methods before our trait methods.
1422 collect(clas->parent());
1424 for (Slot i = numDeclMethods; i < numMethods; ++i) {
1425 // For repo mode, where trait methods are flattened at compile-time.
1426 add(methods[i]);
1428 for (Slot i = clas->traitsBeginIdx(); i < clas->traitsEndIdx(); ++i) {
1429 // For non-repo mode, where they are added at Class-creation time.
1430 add(clas->getMethod(i));
1434 collectInterface = [&] (const Class* iface) {
1435 if (!iface) return;
1437 size_t const numMethods = iface->preClass()->numMethods();
1438 Func* const* methods = iface->preClass()->methods();
1439 for (Slot i = 0; i < numMethods; ++i) {
1440 add(methods[i]);
1443 for (auto const& parentIface: iface->declInterfaces()) {
1444 collectInterface(parentIface.get());
1446 auto const& allIfaces = iface->allInterfaces();
1447 if (allIfaces.size() > iface->declInterfaces().size()) {
1448 for (int i = 0; i < allIfaces.size(); ++i) {
1449 collectInterface(allIfaces[i].get());
1454 collect(const_cast<Class*>(cls));
1456 // concrete classes should already have all of their methods present
1457 if (((AttrPublic | AttrAbstract | AttrStatic) & mask) &&
1458 cls->attrs() & (AttrInterface | AttrAbstract | AttrTrait)) {
1459 for (auto const& interface: cls->declInterfaces()) {
1460 collectInterface(interface.get());
1462 auto const& allIfaces = cls->allInterfaces();
1463 if (allIfaces.size() > cls->declInterfaces().size()) {
1464 for (int i = 0; i < allIfaces.size(); ++i) {
1465 auto const& interface = allIfaces[i];
1466 collectInterface(interface.get());
1470 return Object(std::move(st));
1473 static bool HHVM_METHOD(ReflectionClass, hasConstant, const String& name) {
1474 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1475 return cls->hasConstant(name.get());
1478 static Variant HHVM_METHOD(ReflectionClass, getConstant, const String& name) {
1479 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1480 auto value = cls->clsCnsGet(name.get());
1481 return (value.m_type == KindOfUninit) ? false_varNR : cellAsCVarRef(value);
1484 static
1485 void addClassConstantNames(const Class* cls,
1486 const req::ptr<c_Set>& st,
1487 size_t limit) {
1488 assert(cls && st && (st->size() < limit));
1490 auto numConsts = cls->numConstants();
1492 const Class::Const* consts = cls->constants();
1493 for (size_t i = 0; i < numConsts; i++) {
1494 if (consts[i].cls == cls && !consts[i].isAbstract() &&
1495 !consts[i].isType()) {
1496 st->add(const_cast<StringData*>(consts[i].name.get()));
1499 if ((st->size() < limit) && cls->parent()) {
1500 addClassConstantNames(cls->parent(), st, limit);
1503 auto const& allIfaces = cls->allInterfaces();
1504 auto const numIfaces = allIfaces.size();
1505 for (int i = 0; i < numIfaces && (st->size() < limit); ++i) {
1506 addClassConstantNames(allIfaces[i].get(), st, limit);
1510 // helper for getConstants
1511 static Array HHVM_METHOD(ReflectionClass, getOrderedConstants) {
1512 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1514 size_t numConsts = cls->numConstants();
1515 if (!numConsts) {
1516 return Array::Create();
1519 auto st = req::make<c_Set>();
1520 st->reserve(numConsts);
1522 addClassConstantNames(cls, st, numConsts);
1523 assert(st->size() <= numConsts);
1525 ArrayInit ai(numConsts, ArrayInit::Mixed{});
1526 for (ArrayIter iter(st.get()); iter; ++iter) {
1527 auto constName = iter.first().getStringData();
1528 Cell value = cls->clsCnsGet(constName);
1529 assert(value.m_type != KindOfUninit);
1530 ai.add(constName, cellAsCVarRef(value));
1532 return ai.toArray();
1535 // helper for getAbstractConstantNames
1536 static Array HHVM_METHOD(ReflectionClass, getOrderedAbstractConstants) {
1537 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1539 size_t numConsts = cls->numConstants();
1540 if (!numConsts) {
1541 return Array::Create();
1544 auto st = req::make<c_Set>();
1545 st->reserve(numConsts);
1547 const Class::Const* consts = cls->constants();
1548 for (size_t i = 0; i < numConsts; i++) {
1549 if (consts[i].isAbstract() && !consts[i].isType()) {
1550 st->add(const_cast<StringData*>(consts[i].name.get()));
1554 assert(st->size() <= numConsts);
1555 return st->toArray();
1560 // helper for getTypeConstants/hasTypeConstant
1561 static Array HHVM_METHOD(ReflectionClass, getOrderedTypeConstants) {
1562 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1564 size_t numConsts = cls->numConstants();
1565 if (!numConsts) {
1566 return Array::Create();
1569 auto st = req::make<c_Set>();
1570 st->reserve(numConsts);
1572 const Class::Const* consts = cls->constants();
1573 for (size_t i = 0; i < numConsts; i++) {
1574 if (consts[i].isType()) {
1575 st->add(const_cast<StringData*>(consts[i].name.get()));
1579 assert(st->size() <= numConsts);
1580 return st->toArray();
1583 static Array HHVM_METHOD(ReflectionClass, getAttributes) {
1584 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1585 // UserAttributes are stored exclusively on the PreClass.
1586 auto const pcls = cls->preClass();
1588 auto userAttrs = pcls->userAttributes();
1589 ArrayInit ai(userAttrs.size(), ArrayInit::Mixed{});
1591 for (auto it = userAttrs.begin(); it != userAttrs.end(); ++it) {
1592 ai.set(StrNR(it->first), tvAsCVarRef(&it->second));
1594 return ai.toArray();
1597 static Array HHVM_METHOD(ReflectionClass, getAttributesRecursive) {
1598 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1600 Array ret = Array::Create(); // no reasonable idea about sizing
1602 // UserAttributes are stored in the PreClass, so we must walk the parent
1603 // chain to get all of them; attribute specifications from child classes
1604 // win over parents.
1606 // const pointer to Class => pointer to a (const) Class
1607 Class* currentCls = const_cast<Class*>(cls);
1608 do {
1609 auto const pcls = currentCls->preClass();
1610 for (auto it = pcls->userAttributes().begin();
1611 it != pcls->userAttributes().end(); ++it) {
1612 if (!ret.exists(StrNR(it->first))) {
1613 ret.add(StrNR(it->first), tvAsCVarRef(&it->second));
1616 } while ((currentCls = currentCls->parent()));
1618 return ret;
1621 static Array HHVM_METHOD(ReflectionClass, getClassPropertyInfo) {
1623 * FIXME: This implementation is pretty horrible and should be rewritten
1624 * when ReflectionProperty is ported.
1626 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1628 auto arrProp = Array::Create();
1629 auto arrPriv = Array::Create();
1630 auto arrIdx = Array::Create();
1631 auto arrPrivIdx = Array::Create();
1633 auto const properties = cls->declProperties();
1634 cls->initialize();
1636 auto const& propInitVec = cls->getPropData()
1637 ? *cls->getPropData()
1638 : cls->declPropInit();
1640 auto const nProps = cls->numDeclProperties();
1642 for (Slot i = 0; i < nProps; ++i) {
1643 auto const& prop = properties[i];
1644 auto const& default_val = tvAsCVarRef(&propInitVec[i]);
1645 auto info = Array::Create();
1646 if ((prop.attrs & AttrPrivate) == AttrPrivate) {
1647 if (prop.cls == cls) {
1648 set_instance_prop_info(info, &prop, default_val);
1649 arrPriv.set(StrNR(prop.name), VarNR(info).tv());
1650 arrPrivIdx.set(StrNR(prop.name), prop.idx);
1652 continue;
1654 set_instance_prop_info(info, &prop, default_val);
1655 arrProp.set(StrNR(prop.name), VarNR(info).tv());
1656 arrIdx.set(StrNR(prop.name), prop.idx);
1659 for (auto const& prop : cls->staticProperties()) {
1660 auto info = Array::Create();
1661 if ((prop.attrs & AttrPrivate) == AttrPrivate) {
1662 if (prop.cls == cls) {
1663 set_static_prop_info(info, &prop);
1664 arrPriv.set(StrNR(prop.name), VarNR(info).tv());
1665 arrPrivIdx.set(StrNR(prop.name), prop.idx);
1667 continue;
1669 set_static_prop_info(info, &prop);
1670 arrProp.set(StrNR(prop.name), VarNR(info).tv());
1671 arrIdx.set(StrNR(prop.name), prop.idx);
1674 ArrayInit ret(4, ArrayInit::Mixed{});
1675 ret.set(s_properties, VarNR(arrProp).tv());
1676 ret.set(s_private_properties, VarNR(arrPriv).tv());
1677 ret.set(s_properties_index, VarNR(arrIdx).tv());
1678 ret.set(s_private_properties_index, VarNR(arrPrivIdx).tv());
1679 return ret.toArray();
1682 static Array HHVM_METHOD(ReflectionClass, getDynamicPropertyInfos,
1683 const Object& obj) {
1684 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1685 auto obj_data = obj.get();
1686 assert(obj_data->getVMClass() == cls);
1687 if (!obj_data->hasDynProps()) {
1688 return empty_array();
1691 auto const dynPropArray = obj_data->dynPropArray().get();
1692 ArrayInit ret(dynPropArray->size(), ArrayInit::Mixed{});
1693 for (ArrayIter it(dynPropArray); !it.end(); it.next()) {
1694 Array info = Array::Create();
1695 set_dyn_prop_info(info, it.first(), cls->name());
1696 ret.setValidKey(*it.first().asTypedValue(), VarNR(info).tv());
1698 return ret.toArray();
1701 static String HHVM_METHOD(ReflectionClass, getConstructorName) {
1702 auto const cls = ReflectionClassHandle::GetClassFor(this_);
1703 auto ctor = cls->getDeclaredCtor();
1704 if (!ctor) { return String(); }
1705 auto ret = const_cast<StringData*>(ctor->name());
1706 return String(ret);
1709 void ReflectionClassHandle::wakeup(const Variant& content, ObjectData* obj) {
1710 if (!content.isString()) {
1711 throw Exception("Native data of ReflectionClass should be a class name");
1714 String clsName = content.toString();
1715 String result = init(clsName);
1716 if (result.empty()) {
1717 auto msg = folly::format("Class {} does not exist", clsName).str();
1718 Reflection::ThrowReflectionExceptionObject(String(msg));
1721 // It is possible that $name does not get serialized. If a class derives
1722 // from ReflectionClass and the return value of its __sleep() function does
1723 // not contain 'name', $name gets ignored. So, we restore $name here.
1724 obj->setProp(nullptr, s_name.get(), make_tv<KindOfString>(result.get()));
1727 static Variant reflection_extension_name_get(const Object& this_) {
1728 assertx(Reflection::s_ReflectionExtensionClass);
1729 auto const name = this_->getProp(
1730 Reflection::s_ReflectionExtensionClass,
1731 s___name.get()
1732 ).unboxed();
1733 return tvCastToString(name.tv());
1736 static Native::PropAccessor reflection_extension_Accessors[] = {
1737 {"name", reflection_extension_name_get,
1738 nullptr, nullptr, nullptr}, // name is read only
1739 {nullptr, nullptr, nullptr, nullptr, nullptr}
1742 static Native::PropAccessorMap reflection_extension_accessorsMap
1743 ((Native::PropAccessor*)reflection_extension_Accessors);
1745 struct reflection_extension_PropHandler :
1746 Native::MapPropHandler<reflection_extension_PropHandler> {
1748 static constexpr Native::PropAccessorMap& map =
1749 reflection_extension_accessorsMap;
1752 /////////////////////////////////////////////////////////////////////////////
1753 // class ReflectionTypeConstant
1755 const StaticString s_ReflectionConstHandle("ReflectionConstHandle");
1757 // helper for __construct
1758 static bool HHVM_METHOD(ReflectionTypeConstant, __init,
1759 const Variant& cls_or_obj, const String& const_name) {
1760 auto const cls = get_cls(cls_or_obj);
1761 if (!cls || const_name.isNull()) {
1762 // caller raises exception
1763 return false;
1766 size_t numConsts = cls->numConstants();
1767 const Class::Const* consts = cls->constants();
1769 for (size_t i = 0; i < numConsts; i++) {
1770 if (const_name.same(consts[i].name) && consts[i].isType()) {
1771 auto handle = ReflectionConstHandle::Get(this_);
1772 handle->setConst(&consts[i]);
1773 handle->setClass(cls);
1774 return true;
1778 // caller raises exception
1779 return false;
1782 static String HHVM_METHOD(ReflectionTypeConstant, getName) {
1783 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1784 auto ret = const_cast<StringData*>(cns->name.get());
1785 return String(ret);
1788 static bool HHVM_METHOD(ReflectionTypeConstant, isAbstract) {
1789 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1790 return cns->isAbstract();
1793 // helper for getAssignedTypeText
1794 static String HHVM_METHOD(ReflectionTypeConstant, getAssignedTypeHint) {
1795 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1797 if (isStringType(cns->val.m_type)) {
1798 return String(cns->val.m_data.pstr);
1801 if (isArrayLikeType(cns->val.m_type)) {
1802 auto const cls = cns->cls;
1803 // go to the preclass to find the unresolved TypeStructure to get
1804 // the original assigned type text
1805 auto const preCls = cls->preClass();
1806 auto typeCns = preCls->lookupConstant(cns->name);
1807 assert(typeCns->isType());
1808 assert(!typeCns->isAbstract());
1809 assert(isArrayLikeType(typeCns->val().m_type));
1810 return TypeStructure::toString(Array::attach(typeCns->val().m_data.parr));
1813 return String();
1816 // private helper for getDeclaringClass
1817 static String HHVM_METHOD(ReflectionTypeConstant, getDeclaringClassname) {
1818 auto const cns = ReflectionConstHandle::GetConstFor(this_);
1819 auto cls = cns->cls;
1820 auto ret = const_cast<StringData*>(cls->name());
1821 return String(ret);
1824 // private helper for getClass
1825 static String HHVM_METHOD(ReflectionTypeConstant, getClassname) {
1826 auto const cls = ReflectionConstHandle::GetClassFor(this_);
1827 auto ret = const_cast<StringData*>(cls->name());
1828 return String(ret);
1831 /////////////////////////////////////////////////////////////////////////////
1832 // class ReflectionProperty
1834 const StaticString s_ReflectionPropHandle("ReflectionPropHandle");
1835 const StaticString s_ReflectionSPropHandle("ReflectionSPropHandle");
1837 // helper for __construct:
1838 // returns -1 if class not defined;
1839 // returns -2 if class::property not found (then caller raises exception);
1840 // returns the info array for ReflectionProperty if successfully initialized.
1841 static Variant HHVM_METHOD(ReflectionProperty, __init,
1842 const Variant& cls_or_obj, const String& prop_name) {
1843 auto const cls = get_cls(cls_or_obj);
1844 if (!cls) {
1845 // caller raises exception
1846 return Variant(-1);
1848 if (prop_name.isNull()) {
1849 return Variant(-2);
1852 cls->initialize();
1853 auto const& propInitVec = cls->getPropData()
1854 ? *cls->getPropData()
1855 : cls->declPropInit();
1857 auto const nProps = cls->numDeclProperties();
1858 auto const properties = cls->declProperties();
1860 // index for the candidate property
1861 Slot cInd = -1;
1862 const Class::Prop* cProp = nullptr;
1864 for (Slot i = 0; i < nProps; i++) {
1865 auto const& prop = properties[i];
1866 if (prop_name.same(prop.name)) {
1867 if (cls == prop.cls.get()) {
1868 // found match for the exact child class
1869 cInd = i;
1870 cProp = &prop;
1871 break;
1872 } else if (!(prop.attrs & AttrPrivate)) {
1873 // only inherit non-private properties
1874 cInd = i;
1875 cProp = &prop;
1880 if (cProp != nullptr) {
1881 ReflectionPropHandle::Get(this_)->setProp(cProp);
1882 auto info = Array::Create();
1883 auto const& default_val = tvAsCVarRef(&propInitVec[cInd]);
1884 set_instance_prop_info(info, cProp, default_val);
1885 return Variant(info);
1888 // for static property
1889 const Class::SProp* cSProp = nullptr;
1891 for (auto const& sprop : cls->staticProperties()) {
1892 if (prop_name.same(sprop.name)) {
1893 if (cls == sprop.cls.get()) {
1894 cSProp = &sprop;
1895 break;
1896 } else if (!(sprop.attrs & AttrPrivate)) {
1897 cSProp = &sprop;
1901 if (cSProp != nullptr) {
1902 ReflectionSPropHandle::Get(this_)->setSProp(cSProp);
1903 auto info = Array::Create();
1904 set_static_prop_info(info, cSProp);
1905 return Variant(info);
1908 // check for dynamic properties
1909 if (cls_or_obj.is(KindOfObject)) {
1910 auto obj = cls_or_obj.toCObjRef().get();
1911 assert(cls == obj->getVMClass());
1912 if (obj->hasDynProps()) {
1913 auto const dynPropArray = obj->dynPropArray().get();
1914 for (ArrayIter it(dynPropArray); !it.end(); it.next()) {
1915 if (prop_name.same(it.first().getStringData())) {
1916 auto info = Array::Create();
1917 set_dyn_prop_info(info, it.first(), cls->name());
1918 return Variant(info);
1924 // caller raises exception
1925 return Variant(-2);
1928 /////////////////////////////////////////////////////////////////////////////
1929 // class ReflectionTypeAlias
1931 const StaticString s_ReflectionTypeAliasHandle("ReflectionTypeAliasHandle");
1933 // helper for __construct:
1934 // caller throws exception when return value is false
1935 static String HHVM_METHOD(ReflectionTypeAlias, __init, const String& name) {
1936 auto const typeAliasReq = Unit::loadTypeAlias(name.get());
1938 if (!typeAliasReq) {
1939 return empty_string();
1942 ReflectionTypeAliasHandle::Get(this_)->setTypeAliasReq(typeAliasReq);
1943 return String::attach(const_cast<StringData*>(typeAliasReq->name.get()));
1946 static Array HHVM_METHOD(ReflectionTypeAlias, getTypeStructure) {
1947 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
1948 assert(req);
1949 auto const typeStructure = req->typeStructure;
1950 assertx(!typeStructure.empty());
1951 assertx(typeStructure.isDArray());
1952 return typeStructure;
1955 static String HHVM_METHOD(ReflectionTypeAlias, getAssignedTypeText) {
1956 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
1957 assert(req);
1958 auto const typeStructure = req->typeStructure;
1959 assertx(!typeStructure.empty());
1960 assertx(typeStructure.isDArray());
1961 return TypeStructure::toString(typeStructure);
1964 static Array HHVM_METHOD(ReflectionTypeAlias, getAttributes) {
1965 auto const req = ReflectionTypeAliasHandle::GetTypeAliasReqFor(this_);
1966 assert(req);
1967 auto const userAttrs = req->userAttrs;
1969 ArrayInit ai(userAttrs.size(), ArrayInit::Mixed{});
1970 for (auto& attr : userAttrs) {
1971 ai.set(StrNR(attr.first), tvAsCVarRef(&attr.second));
1973 return ai.toArray();
1976 ///////////////////////////////////////////////////////////////////////////////
1977 struct ReflectionExtension final : Extension {
1978 ReflectionExtension() : Extension("reflection", "$Id$") { }
1979 void moduleInit() override {
1980 HHVM_FE(hphp_create_object);
1981 HHVM_FE(hphp_create_object_without_constructor);
1982 HHVM_FE(hphp_get_extension_info);
1983 HHVM_FE(hphp_get_original_class_name);
1984 HHVM_FE(hphp_get_property);
1985 HHVM_FE(hphp_get_static_property);
1986 HHVM_FE(hphp_invoke);
1987 HHVM_FE(hphp_invoke_method);
1988 HHVM_FE(hphp_set_property);
1989 HHVM_FE(hphp_set_static_property);
1990 HHVM_FALIAS(HH\\type_structure, type_structure);
1992 HHVM_ME(ReflectionFunctionAbstract, getName);
1993 HHVM_ME(ReflectionFunctionAbstract, isHack);
1994 HHVM_ME(ReflectionFunctionAbstract, isInternal);
1995 HHVM_ME(ReflectionFunctionAbstract, isGenerator);
1996 HHVM_ME(ReflectionFunctionAbstract, isAsync);
1997 HHVM_ME(ReflectionFunctionAbstract, isVariadic);
1998 HHVM_ME(ReflectionFunctionAbstract, getFileName);
1999 HHVM_ME(ReflectionFunctionAbstract, getStartLine);
2000 HHVM_ME(ReflectionFunctionAbstract, getEndLine);
2001 HHVM_ME(ReflectionFunctionAbstract, getDocComment);
2002 HHVM_ME(ReflectionFunctionAbstract, getStaticVariables);
2003 HHVM_ME(ReflectionFunctionAbstract, returnsReference);
2004 HHVM_ME(ReflectionFunctionAbstract, getReturnTypeHint);
2005 HHVM_ME(ReflectionFunctionAbstract, getNumberOfParameters);
2006 HHVM_ME(ReflectionFunctionAbstract, getParamInfo);
2007 HHVM_ME(ReflectionFunctionAbstract, getAttributes);
2008 HHVM_ME(ReflectionFunctionAbstract, getRetTypeInfo);
2010 HHVM_ME(ReflectionMethod, __init);
2011 HHVM_ME(ReflectionMethod, isFinal);
2012 HHVM_ME(ReflectionMethod, isAbstract);
2013 HHVM_ME(ReflectionMethod, isPublic);
2014 HHVM_ME(ReflectionMethod, isProtected);
2015 HHVM_ME(ReflectionMethod, isPrivate);
2016 HHVM_ME(ReflectionMethod, isStatic);
2017 HHVM_ME(ReflectionMethod, isConstructor);
2018 HHVM_ME(ReflectionMethod, getModifiers);
2019 HHVM_ME(ReflectionMethod, getPrototypeClassname);
2020 HHVM_ME(ReflectionMethod, getDeclaringClassname);
2022 HHVM_ME(ReflectionFunction, __initName);
2023 HHVM_ME(ReflectionFunction, __initClosure);
2024 HHVM_ME(ReflectionFunction, getClosureUseVariables);
2025 HHVM_ME(ReflectionFunction, getClosureScopeClassname);
2026 HHVM_ME(ReflectionFunction, getClosureThisObject);
2028 HHVM_ME(ReflectionTypeConstant, __init);
2029 HHVM_ME(ReflectionTypeConstant, getName);
2030 HHVM_ME(ReflectionTypeConstant, isAbstract);
2031 HHVM_ME(ReflectionTypeConstant, getAssignedTypeHint);
2032 HHVM_ME(ReflectionTypeConstant, getDeclaringClassname);
2033 HHVM_ME(ReflectionTypeConstant, getClassname);
2035 HHVM_ME(ReflectionProperty, __init);
2037 HHVM_ME(ReflectionTypeAlias, __init);
2038 HHVM_ME(ReflectionTypeAlias, getTypeStructure);
2039 HHVM_ME(ReflectionTypeAlias, getAttributes);
2040 HHVM_ME(ReflectionTypeAlias, getAssignedTypeText);
2042 HHVM_ME(ReflectionClass, __init);
2043 HHVM_ME(ReflectionClass, getName);
2044 HHVM_ME(ReflectionClass, getParentName);
2045 HHVM_ME(ReflectionClass, isHack);
2046 HHVM_ME(ReflectionClass, isInternal);
2047 HHVM_ME(ReflectionClass, isInstantiable);
2048 HHVM_ME(ReflectionClass, isInterface);
2049 HHVM_ME(ReflectionClass, isTrait);
2050 HHVM_ME(ReflectionClass, isEnum);
2051 HHVM_ME(ReflectionClass, isAbstract);
2052 HHVM_ME(ReflectionClass, isFinal);
2053 HHVM_ME(ReflectionClass, getModifiers);
2054 HHVM_ME(ReflectionClass, getFileName);
2055 HHVM_ME(ReflectionClass, getStartLine);
2056 HHVM_ME(ReflectionClass, getEndLine);
2057 HHVM_ME(ReflectionClass, getDocComment);
2058 HHVM_ME(ReflectionClass, getInterfaceNames);
2059 HHVM_ME(ReflectionClass, getRequirementNames);
2060 HHVM_ME(ReflectionClass, getTraitNames);
2061 HHVM_ME(ReflectionClass, getTraitAliases);
2063 HHVM_ME(ReflectionClass, hasMethod);
2064 HHVM_ME(ReflectionClass, getMethodOrder);
2066 HHVM_ME(ReflectionClass, hasConstant);
2067 HHVM_ME(ReflectionClass, getConstant);
2068 HHVM_ME(ReflectionClass, getOrderedConstants);
2069 HHVM_ME(ReflectionClass, getOrderedAbstractConstants);
2070 HHVM_ME(ReflectionClass, getOrderedTypeConstants);
2072 HHVM_ME(ReflectionClass, getAttributes);
2073 HHVM_ME(ReflectionClass, getAttributesRecursive);
2075 HHVM_ME(ReflectionClass, getClassPropertyInfo);
2076 HHVM_ME(ReflectionClass, getDynamicPropertyInfos);
2077 HHVM_ME(ReflectionClass, getConstructorName);
2079 Native::registerNativeDataInfo<ReflectionFuncHandle>(
2080 s_ReflectionFuncHandle.get());
2081 Native::registerNativeDataInfo<ReflectionClassHandle>(
2082 s_ReflectionClassHandle.get());
2083 Native::registerNativeDataInfo<ReflectionConstHandle>(
2084 s_ReflectionConstHandle.get());
2085 Native::registerNativeDataInfo<ReflectionPropHandle>(
2086 s_ReflectionPropHandle.get());
2087 Native::registerNativeDataInfo<ReflectionSPropHandle>(
2088 s_ReflectionSPropHandle.get());
2089 Native::registerNativeDataInfo<ReflectionTypeAliasHandle>(
2090 s_ReflectionTypeAliasHandle.get(), Native::NO_SWEEP);
2092 Native::registerNativePropHandler
2093 <reflection_extension_PropHandler>(s_reflectionextension);
2095 loadSystemlib();
2096 loadSystemlib("reflection-classes");
2097 loadSystemlib("reflection-internals-functions");
2098 loadSystemlib("reflection_hni");
2100 Reflection::s_ReflectionExceptionClass =
2101 Unit::lookupClass(s_reflectionexception.get());
2102 assertx(Reflection::s_ReflectionExceptionClass);
2103 Reflection::s_ReflectionExtensionClass =
2104 Unit::lookupClass(s_reflectionextension.get());
2105 assertx(Reflection::s_ReflectionExtensionClass);
2107 } s_reflection_extension;
2109 ///////////////////////////////////////////////////////////////////////////////
2111 namespace DebuggerReflection {
2113 static bool set_debugger_source_info(Array &ret, const char *file, int line1,
2114 int line2) {
2115 if (!file) file = "";
2116 if (file[0] != '/') {
2117 ret.set(s_file, String(RuntimeOption::SourceRoot + file));
2118 } else {
2119 ret.set(s_file, file);
2121 ret.set(s_line1, make_tv<KindOfInt64>(line1));
2122 ret.set(s_line2, make_tv<KindOfInt64>(line2));
2123 return file && *file;
2126 static void set_debugger_return_type_constraint(Array &ret, const StringData* retType) {
2127 if (retType && retType->size()) {
2128 assertx(!retType->isRefCounted());
2129 ret.set(s_return_type, make_tv<KindOfPersistentString>(retType));
2130 } else {
2131 ret.set(s_return_type, false_varNR.tv());
2135 static void set_debugger_reflection_method_prototype_info(Array& ret,
2136 const Func *func) {
2137 const Class *prototypeCls = nullptr;
2138 if (func->baseCls() != nullptr && func->baseCls() != func->implCls()) {
2139 prototypeCls = func->baseCls();
2140 const Class *result = get_prototype_class_from_interfaces(
2141 prototypeCls, func);
2142 if (result) prototypeCls = result;
2143 } else if (func->isMethod()) {
2144 // lookup the prototype in the interfaces
2145 prototypeCls = get_prototype_class_from_interfaces(func->implCls(), func);
2147 if (prototypeCls) {
2148 Array prototype = Array::Create();
2149 prototype.set(s_class,
2150 make_tv<KindOfPersistentString>(prototypeCls->name()));
2151 prototype.set(s_name, make_tv<KindOfPersistentString>(func->name()));
2152 ret.set(s_prototype, prototype);
2156 static void set_debugger_reflection_function_info(Array& ret,
2157 const Func* func) {
2158 // return type
2159 if (func->attrs() & AttrReference) {
2160 ret.set(s_ref, true_varNR.tv());
2162 if (func->isBuiltin()) {
2163 ret.set(s_internal, true_varNR.tv());
2165 set_debugger_return_type_constraint(ret, func->returnUserType());
2167 // doc comments
2168 set_doc_comment(ret, func->docComment(), func->isBuiltin());
2170 // parameters
2171 ret.set(s_params, get_function_param_info(func));
2173 // static variables
2174 ret.set(s_static_variables, get_function_static_variables(func));
2176 // user attributes
2177 ret.set(s_attributes, get_function_user_attributes(func));
2179 ret.set(s_is_async, func->isAsync());
2180 ret.set(s_is_closure, func->isClosureBody());
2181 ret.set(s_is_generator, func->isGenerator());
2184 static void set_debugger_reflection_method_info(Array& ret, const Func* func,
2185 const Class* cls) {
2186 ret.set(s_name, make_tv<KindOfPersistentString>(func->name()));
2187 set_attrs(ret, get_modifiers(func->attrs(), false));
2189 if (isConstructor(func)) {
2190 ret.set(s_constructor, true_varNR.tv());
2193 // If Func* is from a PreClass, it doesn't know about base classes etc.
2194 // Swap it out for the full version if possible.
2195 auto resolved_func = func->implCls()
2196 ? func
2197 : cls->lookupMethod(func->name());
2199 if (!resolved_func) {
2200 resolved_func = func;
2203 ret.set(s_class,
2204 make_tv<KindOfPersistentString>(resolved_func->implCls()->name()));
2205 set_debugger_reflection_function_info(ret, resolved_func);
2206 set_debugger_source_info(ret, func->unit()->filepath()->data(),
2207 func->line1(), func->line2());
2208 set_debugger_reflection_method_prototype_info(ret, resolved_func);
2211 Array get_function_info(const String& name) {
2212 Array ret;
2213 if (name.get() == nullptr) return ret;
2214 const Func* func = Unit::loadFunc(name.get());
2215 if (!func) return ret;
2216 ret.set(s_name, make_tv<KindOfPersistentString>(func->name()));
2218 // setting parameters and static variables
2219 set_debugger_reflection_function_info(ret, func);
2220 set_debugger_source_info(ret, func->unit()->filepath()->data(),
2221 func->line1(), func->line2());
2222 return ret;
2225 Array get_class_info(const String& name) {
2226 auto cls = get_cls(name);
2227 if (!cls) return Array();
2229 Array ret;
2230 ret.set(s_name, make_tv<KindOfPersistentString>(cls->name()));
2231 ret.set(s_extension, empty_string_variant_ref);
2232 ret.set(s_parent, make_tv<KindOfPersistentString>(cls->preClass()->parent()));
2234 // interfaces
2236 Array arr = Array::Create();
2237 for (auto const& interface: cls->declInterfaces()) {
2238 arr.set(interface->nameStr(), make_tv<KindOfInt64>(1));
2240 auto const& allIfaces = cls->allInterfaces();
2241 if (allIfaces.size() > cls->declInterfaces().size()) {
2242 for (int i = 0; i < allIfaces.size(); ++i) {
2243 auto const& interface = allIfaces[i];
2244 arr.set(interface->nameStr(), make_tv<KindOfInt64>(1));
2247 ret.set(s_interfaces, VarNR(arr).tv());
2250 // traits
2252 Array arr = Array::Create();
2253 for (auto const& traitName : cls->preClass()->usedTraits()) {
2254 arr.set(StrNR(traitName), make_tv<KindOfInt64>(1));
2256 ret.set(s_traits, VarNR(arr).tv());
2259 // trait aliases
2261 ret.set(s_trait_aliases, VarNR(get_trait_alias_info(cls)).tv());
2264 // attributes
2266 if (cls->attrs() & AttrBuiltin) {
2267 ret.set(s_internal, true_varNR.tv());
2269 if (cls->attrs() & AttrFinal) {
2270 ret.set(s_final, true_varNR.tv());
2272 if (cls->attrs() & AttrAbstract) {
2273 ret.set(s_abstract, true_varNR.tv());
2275 if (cls->attrs() & AttrInterface) {
2276 ret.set(s_interface, true_varNR.tv());
2278 if (cls->attrs() & AttrTrait) {
2279 ret.set(s_trait, true_varNR.tv());
2281 ret.set(s_modifiers, make_tv<KindOfInt64>(
2282 get_modifiers(cls->attrs(), true))
2285 if (cls->getCtor()->attrs() & AttrPublic &&
2286 !(cls->attrs() & AttrAbstract) &&
2287 !(cls->attrs() & AttrInterface) &&
2288 !(cls->attrs() & AttrTrait)) {
2289 ret.set(s_instantiable, true_varNR.tv());
2293 // methods
2295 Array arr = Array::Create();
2297 // Fetch from PreClass as:
2298 // - the order is important
2299 // - we want type profiling info
2300 // and neither of these are in the Class...
2301 Func* const* methods = cls->preClass()->methods();
2302 size_t const numMethods = cls->preClass()->numMethods();
2303 for (Slot i = 0; i < numMethods; ++i) {
2304 const Func* m = methods[i];
2305 if (m->isGenerated()) continue;
2307 auto lowerName = HHVM_FN(strtolower)(m->nameStr());
2308 Array info = Array::Create();
2309 set_debugger_reflection_method_info(info, m, cls);
2310 arr.set(lowerName, VarNR(info).tv());
2313 for (Slot i = cls->traitsBeginIdx(); i < cls->traitsEndIdx(); ++i) {
2314 const Func* m = cls->getMethod(i);
2315 if (m->isGenerated()) continue;
2317 auto lowerName = HHVM_FN(strtolower)(m->nameStr());
2318 Array info = Array::Create();
2319 set_debugger_reflection_method_info(info, m, cls);
2320 arr.set(lowerName, VarNR(info).tv());
2322 ret.set(s_methods, VarNR(arr).tv());
2325 // properties
2327 auto arr = Array::Create();
2328 auto arrPriv = Array::Create();
2329 auto arrIdx = Array::Create();
2330 auto arrPrivIdx = Array::Create();
2332 auto const properties = cls->declProperties();
2333 auto const& propInitVec = cls->declPropInit();
2334 auto const nProps = cls->numDeclProperties();
2336 for (Slot i = 0; i < nProps; ++i) {
2337 auto const& prop = properties[i];
2338 auto const& default_val = tvAsCVarRef(&propInitVec[i]);
2339 auto info = Array::Create();
2340 if ((prop.attrs & AttrPrivate) == AttrPrivate) {
2341 if (prop.cls == cls) {
2342 set_instance_prop_info(info, &prop, default_val);
2343 arrPriv.set(StrNR(prop.name), VarNR(info).tv());
2344 arrPrivIdx.set(StrNR(prop.name), prop.idx);
2346 continue;
2348 set_instance_prop_info(info, &prop, default_val);
2349 arr.set(StrNR(prop.name), VarNR(info).tv());
2350 arrIdx.set(StrNR(prop.name), prop.idx);
2353 for (auto const& prop : cls->staticProperties()) {
2354 auto info = Array::Create();
2355 if ((prop.attrs & AttrPrivate) == AttrPrivate) {
2356 if (prop.cls == cls) {
2357 set_static_prop_info(info, &prop);
2358 arrPriv.set(StrNR(prop.name), VarNR(info).tv());
2359 arrPrivIdx.set(StrNR(prop.name), prop.idx);
2361 continue;
2363 set_static_prop_info(info, &prop);
2364 arr.set(StrNR(prop.name), VarNR(info).tv());
2365 arrIdx.set(StrNR(prop.name), prop.idx);
2367 ret.set(s_properties, VarNR(arr).tv());
2368 ret.set(s_private_properties, VarNR(arrPriv).tv());
2369 ret.set(s_properties_index, VarNR(arrIdx).tv());
2370 ret.set(s_private_properties_index, VarNR(arrPrivIdx).tv());
2373 // constants
2375 Array arr = Array::Create();
2377 size_t numConsts = cls->numConstants();
2378 const Class::Const* consts = cls->constants();
2380 for (size_t i = 0; i < numConsts; i++) {
2381 // Note: hphpc doesn't include inherited constants in
2382 // get_class_constants(), so mimic that behavior
2383 if (consts[i].cls == cls) {
2384 Cell value = cls->clsCnsGet(consts[i].name);
2385 assert(value.m_type != KindOfUninit);
2386 arr.set(StrNR(consts[i].name), cellAsCVarRef(value));
2390 ret.set(s_constants, VarNR(arr).tv());
2393 { // source info
2394 const PreClass* pcls = cls->preClass();
2395 set_debugger_source_info(ret, pcls->unit()->filepath()->data(),
2396 pcls->line1(), pcls->line2());
2397 set_doc_comment(ret, pcls->docComment(), pcls->isBuiltin());
2400 // user attributes
2402 Array arr = Array::Create();
2403 const PreClass* pcls = cls->preClass();
2404 for (auto it = pcls->userAttributes().begin();
2405 it != pcls->userAttributes().end(); ++it) {
2406 arr.set(StrNR(it->first), tvAsCVarRef(&it->second));
2408 ret.set(s_attributes, VarNR(arr).tv());
2411 return ret;
2416 ///////////////////////////////////////////////////////////////////////////////