Permalink
Browse files

Allow reflection to work on closures.

This allows you to pass a closure to the constructor
of ReflectionFunction in place of a function name.
  • Loading branch information...
1 parent 866b71a commit 479a20fb3a51b8aae5bd0217b2cf08ca24f76796 psnell committed with sgolemon Oct 10, 2012
View
@@ -3604,9 +3604,13 @@ class ReflectionFunction extends ReflectionFunctionAbstract
const IS_DEPRECATED = 262144;
public function __construct($name) {
- $this->info = hphp_get_function_info($name);
- if (empty($this->info)) {
- throw new ReflectionException("Function $name does not exist");
+ if ($name instanceof Closure) {
+ $this->info = hphp_get_closure_info($name);
+ } else {
+ $this->info = hphp_get_function_info($name);
+ if (empty($this->info)) {
+ throw new ReflectionException("Function $name does not exist");
+ }
}
}
@@ -3649,6 +3653,11 @@ public static function export($name, $ret=false) {
*/
public function invoke() {
$args = func_get_args();
+ if (isset($this->info['closureobj'])) {
+ $closure = $this->info['closureobj'];
+ return hphp_invoke_method($closure, get_class($closure),
+ '__invoke', $args);
+ }
return hphp_invoke($this->info['name'], $args);
}
@@ -3663,6 +3672,11 @@ public function invoke() {
* @args mixed The args to invoke.
*/
public function invokeArgs($args) {
+ if (isset($this->info['closureobj'])) {
+ $closure = $this->info['closureobj'];
+ return hphp_invoke_method($closure, get_class($closure),
+ '__invoke', array_values($args));
+ }
return hphp_invoke($this->info['name'], array_values($args));
}
@@ -4482,7 +4496,7 @@ public function getExtension() {
public function getExtensionName() {
return $this->fetch('extension')->getName();
}
-
+
public function getAttribute($name) {
$attrs = $this->fetch('attributes');
return isset($attrs[$name]) ? $attrs[$name] : null;
@@ -4491,12 +4505,12 @@ public function getAttribute($name) {
public function getAttributes() {
return $this->fetch('attributes');
}
-
+
public function getAttributeRecursive($name) {
$attrs = $this->fetch('attributes_rec');
return isset($attrs[$name]) ? $attrs[$name] : null;
}
-
+
public function getAttributesRecursive() {
return $this->fetch('attributes_rec');
}
@@ -5060,7 +5074,7 @@ public function getAttribute($name) {
public function getAttributes() {
return $this->info['attributes'];
}
-
+
public function getAttributeRecursive($name) {
$attrs = $this->info['attributes'];
if (isset($attrs[$name])) {
@@ -5076,7 +5090,7 @@ public function getAttributeRecursive($name) {
}
return $rm->getAttributeRecursive($name);
}
-
+
public function getAttributesRecursive() {
$attrs = $this->info['attributes'];
$p = get_parent_class($this->class);
@@ -2203,7 +2203,7 @@ void FunctionScope::outputCPPPreface(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("\n");
}
- cg_printf("%sClosure$%s(const CallInfo *func, void *extra",
+ cg_printf("%sClosure$%s(const CallInfo *func, const char *name",
Option::ClassPrefix, funcName.c_str());
if (!useVars.empty()) cg_printf(", ");
@@ -2217,7 +2217,7 @@ void FunctionScope::outputCPPPreface(CodeGenerator &cg, AnalysisResultPtr ar) {
// TODO: non ref variants can be directly assigned to the member
// variable in the initialization list, giving an ever-so-slight
// gain in performance
- cg_printf(") : %sClosure(func, extra)", Option::ClassPrefix);
+ cg_printf(") : %sClosure(func, name)", Option::ClassPrefix);
if (variables->hasStaticLocals()) {
cg_printf(", ");
variables->outputCPPStaticLocals(cg, ar, true);
@@ -283,15 +283,17 @@ void ClosureExpression::outputCPPImpl(CodeGenerator &cg,
const string &origName = m_func->getOriginalName();
if (needsAnonCls) {
- cg_printf("%sClosure$%s(NEWOBJ(%sClosure$%s)(&%s%s, NULL",
+ cg_printf("%sClosure$%s(NEWOBJ(%sClosure$%s)(&%s%s, \"%s\"",
Option::SmartPtrPrefix, origName.c_str(),
Option::ClassPrefix, origName.c_str(),
- Option::CallInfoPrefix, origName.c_str());
+ Option::CallInfoPrefix, origName.c_str(),
+ origName.c_str());
} else {
// no use vars, so can use the generic closure
- cg_printf("%sClosure(NEWOBJ(%sClosure)(&%s%s, NULL",
+ cg_printf("%sClosure(NEWOBJ(%sClosure)(&%s%s, \"%s\"",
Option::SmartPtrPrefix, Option::ClassPrefix,
- Option::CallInfoPrefix, origName.c_str());
+ Option::CallInfoPrefix, origName.c_str(),
+ origName.c_str());
}
bool hasEmit = false;
View
@@ -95,25 +95,22 @@
*/
virtual const CallInfo *t___invokeCallInfoHelper(void *&extra);
- /**
- * Used by HPHPI to make closures work
- */
- void *extraData() const { return m_extraData; }
+ String name() const { return String(m_name, CopyString); }
/**
* This is the constructor which is called internally-
* PHP code will never be able to call this constructor
*/
- c_Closure(const CallInfo *callInfo, void *extraData,
+ c_Closure(const CallInfo *callInfo, const char *name,
const ObjectStaticCallbacks *cb = &cw_Closure) :
- ExtObjectData(cb), m_callInfo(callInfo), m_extraData(extraData) {
+ ExtObjectData(cb), m_callInfo(callInfo), m_name(name) {
ASSERT(callInfo);
}
protected:
virtual bool php_sleep(Variant &ret);
private:
const CallInfo *m_callInfo;
- void *m_extraData;
+ const char *m_name;
EOT
,
)
View
@@ -76,7 +76,7 @@
),
'args' => array(
array(
- 'name' => "cls",
+ 'name' => "cname",
'type' => Variant,
),
array(
@@ -89,6 +89,23 @@
DefineFunction(
array(
+ 'name' => "hphp_get_closure_info",
+ 'desc' => "Internally used by ReflectionFunction for getting a closure's information.",
+ 'flags' => HasDocComment | HipHopSpecific | NoInjection,
+ 'return' => array(
+ 'type' => VariantMap,
+ ),
+ 'args' => array(
+ array(
+ 'name' => "closure",
+ 'type' => Variant,
+ ),
+ ),
+ 'taint_observer' => false,
+ ));
+
+DefineFunction(
+ array(
'name' => "hphp_get_class_constant",
'desc' => "Internally used by ReflectionClass for getting a default parameters value.",
'flags' => HasDocComment | HipHopSpecific | NoInjection,
@@ -52,7 +52,10 @@ class GarbageList {
void* maybePop() {
void** ret = ptr;
if (LIKELY(ret != NULL)) {
- ptr = (void**)*ret;
+ void** next_free = (void**)*ret;
+ ptr = next_free;
+ // prefetch the next-free-object's next pointer
+ __builtin_prefetch(next_free,0,1); // rw=0, locality=T2
}
return ret;
}
@@ -55,25 +55,22 @@ class c_Closure : public ExtObjectData {
*/
virtual const CallInfo *t___invokeCallInfoHelper(void *&extra);
- /**
- * Used by HPHPI to make closures work
- */
- void *extraData() const { return m_extraData; }
+ String name() const { return String(m_name, CopyString); }
/**
* This is the constructor which is called internally-
* PHP code will never be able to call this constructor
*/
- c_Closure(const CallInfo *callInfo, void *extraData,
+ c_Closure(const CallInfo *callInfo, const char *name,
const ObjectStaticCallbacks *cb = &cw_Closure) :
- ExtObjectData(cb), m_callInfo(callInfo), m_extraData(extraData) {
+ ExtObjectData(cb), m_callInfo(callInfo), m_name(name) {
ASSERT(callInfo);
}
protected:
virtual bool php_sleep(Variant &ret);
private:
const CallInfo *m_callInfo;
- void *m_extraData;
+ const char *m_name;
};
///////////////////////////////////////////////////////////////////////////////
@@ -16,6 +16,7 @@
*/
#include <runtime/ext/ext_reflection.h>
+#include <runtime/ext/ext_closure.h>
#include <runtime/base/externals.h>
#include <runtime/base/class_info.h>
#include <runtime/base/source_info.h>
@@ -75,6 +76,9 @@ static StaticString s_attributes("attributes");
static StaticString s_trait_aliases("trait_aliases");
static StaticString s_varg("varg");
static StaticString s_closure("closure");
+static StaticString s___invoke("__invoke");
+static StaticString s_closure_in_braces("{closure}");
+static StaticString s_closureobj("closureobj");
static const VM::Class* get_cls(CVarRef class_or_object) {
VM::Class* cls = NULL;
@@ -452,6 +456,33 @@ Array f_hphp_get_method_info(CVarRef cls, CVarRef name) {
}
}
+Array f_hphp_get_closure_info(CVarRef closure) {
+ if (hhvm) {
+ Array mi = f_hphp_get_method_info(closure.toObject()->o_getClassName(),
+ s___invoke);
+ mi.set(s_name, s_closure_in_braces);
+ mi.set(s_closureobj, closure);
+ mi.set(s_closure, empty_string);
+ mi.remove(s_access);
+ mi.remove(s_modifiers);
+ mi.remove(s_class);
+
+ Array &params = mi.lvalAt(s_params).asArrRef();
+ for (int i = 0; i < params.size(); i++) {
+ params.lvalAt(i).asArrRef().remove(s_class);
+ }
+
+ return mi;
+ }
+
+ const c_Closure *c = closure.asCObjRef().getTyped<const c_Closure>();
+ Array fi = f_hphp_get_function_info(c->name());
+ fi.set(s_name, s_closure_in_braces);
+ fi.set(s_closureobj, closure);
+ fi.remove(s_varg);
+ return fi;
+}
+
Variant f_hphp_get_class_constant(CVarRef cls, CVarRef name) {
return get_class_constant(cls, name.toString().data());
}
@@ -85,11 +85,11 @@ _ZN4HPHP22f_hphp_get_method_infoERKNS_7VariantES2_
(return value) => rax
_rv => rdi
-cls => rsi
+cname => rsi
name => rdx
*/
-Value* fh_hphp_get_method_info(Value* _rv, TypedValue* cls, TypedValue* name) asm("_ZN4HPHP22f_hphp_get_method_infoERKNS_7VariantES2_");
+Value* fh_hphp_get_method_info(Value* _rv, TypedValue* cname, TypedValue* name) asm("_ZN4HPHP22f_hphp_get_method_infoERKNS_7VariantES2_");
TypedValue* fg_hphp_get_method_info(HPHP::VM::ActRec *ar) {
TypedValue rv;
@@ -118,6 +118,43 @@ TypedValue* fg_hphp_get_method_info(HPHP::VM::ActRec *ar) {
/*
+HPHP::Array HPHP::f_hphp_get_closure_info(HPHP::Variant const&)
+_ZN4HPHP23f_hphp_get_closure_infoERKNS_7VariantE
+
+(return value) => rax
+_rv => rdi
+closure => rsi
+*/
+
+Value* fh_hphp_get_closure_info(Value* _rv, TypedValue* closure) asm("_ZN4HPHP23f_hphp_get_closure_infoERKNS_7VariantE");
+
+TypedValue* fg_hphp_get_closure_info(HPHP::VM::ActRec *ar) {
+ TypedValue rv;
+ long long count = ar->numArgs();
+ TypedValue* args UNUSED = ((TypedValue*)ar) - 1;
+ if (count == 1LL) {
+ rv._count = 0;
+ rv.m_type = KindOfArray;
+ fh_hphp_get_closure_info((Value*)(&(rv)), (args-0));
+ if (rv.m_data.num == 0LL) rv.m_type = KindOfNull;
+ frame_free_locals_no_this_inl(ar, 1);
+ memcpy(&ar->m_r, &rv, sizeof(TypedValue));
+ return &ar->m_r;
+ } else {
+ throw_wrong_arguments_nr("hphp_get_closure_info", count, 1, 1, 1);
+ }
+ rv.m_data.num = 0LL;
+ rv._count = 0;
+ rv.m_type = KindOfNull;
+ frame_free_locals_no_this_inl(ar, 1);
+ memcpy(&ar->m_r, &rv, sizeof(TypedValue));
+ return &ar->m_r;
+ return &ar->m_r;
+}
+
+
+
+/*
HPHP::Variant HPHP::f_hphp_get_class_constant(HPHP::Variant const&, HPHP::Variant const&)
_ZN4HPHP25f_hphp_get_class_constantERKNS_7VariantES2_
@@ -26,7 +26,8 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
Array f_hphp_get_extension_info(CStrRef name);
-Array f_hphp_get_method_info(CVarRef cls, CVarRef name);
+Array f_hphp_get_method_info(CVarRef cname, CVarRef name);
+Array f_hphp_get_closure_info(CVarRef closure);
Variant f_hphp_get_class_constant(CVarRef cls, CVarRef name);
Array f_hphp_get_class_info(CVarRef name);
Array f_hphp_get_function_info(CStrRef name);
@@ -29,8 +29,12 @@ inline Array x_hphp_get_extension_info(CStrRef name) {
return f_hphp_get_extension_info(name);
}
-inline Array x_hphp_get_method_info(CVarRef cls, CVarRef name) {
- return f_hphp_get_method_info(cls, name);
+inline Array x_hphp_get_method_info(CVarRef cname, CVarRef name) {
+ return f_hphp_get_method_info(cname, name);
+}
+
+inline Array x_hphp_get_closure_info(CVarRef closure) {
+ return f_hphp_get_closure_info(closure);
}
inline Variant x_hphp_get_class_constant(CVarRef cls, CVarRef name) {
@@ -1772,6 +1772,7 @@ TypedValue* fg_escapeshellarg(VM::ActRec *ar);
TypedValue* fg_escapeshellcmd(VM::ActRec *ar);
TypedValue* fg_hphp_get_extension_info(VM::ActRec *ar);
TypedValue* fg_hphp_get_method_info(VM::ActRec *ar);
+TypedValue* fg_hphp_get_closure_info(VM::ActRec *ar);
TypedValue* fg_hphp_get_class_constant(VM::ActRec *ar);
TypedValue* fg_hphp_get_class_info(VM::ActRec *ar);
TypedValue* fg_hphp_get_function_info(VM::ActRec *ar);
@@ -2912,7 +2913,7 @@ TypedValue* tg_9XMLWriter_endDTD(VM::ActRec *ar);
TypedValue* tg_9XMLWriter_flush(VM::ActRec *ar);
TypedValue* tg_9XMLWriter_outputMemory(VM::ActRec *ar);
-const long long hhbc_ext_funcs_count = 2184;
+const long long hhbc_ext_funcs_count = 2185;
const HhbcExtFuncInfo hhbc_ext_funcs[] = {
{ "apache_note", fg_apache_note },
{ "apache_request_headers", fg_apache_request_headers },
@@ -4664,6 +4665,7 @@ const HhbcExtFuncInfo hhbc_ext_funcs[] = {
{ "escapeshellcmd", fg_escapeshellcmd },
{ "hphp_get_extension_info", fg_hphp_get_extension_info },
{ "hphp_get_method_info", fg_hphp_get_method_info },
+ { "hphp_get_closure_info", fg_hphp_get_closure_info },
{ "hphp_get_class_constant", fg_hphp_get_class_constant },
{ "hphp_get_class_info", fg_hphp_get_class_info },
{ "hphp_get_function_info", fg_hphp_get_function_info },
Oops, something went wrong.

0 comments on commit 479a20f

Please sign in to comment.