From 7b7fe4c408021c789f4f940642722625f4eebdda Mon Sep 17 00:00:00 2001 From: Phil Saltzman Date: Mon, 18 Aug 2014 15:06:32 -0700 Subject: [PATCH] Makes <<__Memoize>> work with static methods Summary: Next will be top level methods. The tests feel a little thin here, but I think the main memoization tests cover most of what needs to be tested so I just added tests to show that it works with a static method. Let me know what you think I'm missing Reviewed By: @jano Differential Revision: D1502068 --- hphp/compiler/analysis/emitter.cpp | 52 ++++++++++++++++++++---------- hphp/compiler/analysis/emitter.h | 3 +- hphp/test/quick/memoize_static.php | 8 +++-- hphp/test/quick/memoize_static.php.expect | 1 + hphp/test/quick/memoize_static.php.expectf | 2 -- 5 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 hphp/test/quick/memoize_static.php.expect delete mode 100644 hphp/test/quick/memoize_static.php.expectf diff --git a/hphp/compiler/analysis/emitter.cpp b/hphp/compiler/analysis/emitter.cpp index 751f6bc4009..096cb45d691 100644 --- a/hphp/compiler/analysis/emitter.cpp +++ b/hphp/compiler/analysis/emitter.cpp @@ -6408,7 +6408,9 @@ void EmitterVisitor::emitPostponedMeths() { folly::sformat("{}$memoize_cache", fe->name->data())); TypedValue tvNull; tvWriteNull(&tvNull); - pce->addProperty(propName, AttrPrivate, nullptr, nullptr, + Attr attrs = AttrPrivate; + attrs = attrs | (funcScope->isStatic() ? AttrStatic : AttrNone); + pce->addProperty(propName, attrs, nullptr, nullptr, &tvNull, RepoAuthType{}); // Rename the method and create a new method with the original name @@ -6784,12 +6786,21 @@ void EmitterVisitor::emitMethodDVInitializers(Emitter& e, if (hasOptional) e.JmpNS(topOfBody); } -void EmitterVisitor::emitMemoizeProp(Emitter &e, const StringData *propName) { - m_evalStack.push(StackSym::H); - m_evalStack.setKnownCls(m_curFunc->pce()->name(), false); - m_evalStack.push(StackSym::T); - m_evalStack.setString(propName); - markProp(e); +void EmitterVisitor::emitMemoizeProp(Emitter &e, + MethodStatementPtr meth, + const StringData *propName) { + if (meth->getFunctionScope()->isStatic()) { + m_evalStack.push(StackSym::K); + m_evalStack.setClsBaseType(SymbolicStack::CLS_SELF); + e.String(propName); + markSProp(e); + } else { + m_evalStack.push(StackSym::H); + m_evalStack.setKnownCls(m_curFunc->pce()->name(), false); + m_evalStack.push(StackSym::T); + m_evalStack.setString(propName); + markProp(e); + } } void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth, @@ -6799,10 +6810,6 @@ void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth, throw IncludeTimeFatalException(meth, "<<__Memoize>> currently only supports methods with zero args"); } - if (meth->getFunctionScope()->isStatic()) { - throw IncludeTimeFatalException(meth, - "<<__Memoize>> cannot be used on static methods yet"); - } if (meth->getFunctionScope()->isRefReturn()) { throw IncludeTimeFatalException(meth, "<<__Memoize>> cannot be used on methods that return by reference"); @@ -6819,20 +6826,31 @@ void EmitterVisitor::emitMemoizeMethod(MethodStatementPtr meth, emitMethodPrologue(e, meth); // If the cache var is non-null return it - e.CheckThis(); - emitMemoizeProp(e, propName); + if (!meth->getFunctionScope()->isStatic()) { + e.CheckThis(); + } + emitMemoizeProp(e, meth, propName); emitCGet(e); e.IsTypeC(IsTypeOp::Null); e.JmpNZ(cacheMiss); - emitMemoizeProp(e, propName); + emitMemoizeProp(e, meth, propName); emitCGet(e); e.RetC(); // Otherwise, call the memoized func, store the result, and return it cacheMiss.set(e); - emitMemoizeProp(e, propName); - e.This(); - emitConstMethodCallNoParams(e, methName->toCppString()); + emitMemoizeProp(e, meth, propName); + if (meth->getFunctionScope()->isStatic()) { + emitClsIfSPropBase(e); + auto fpiStart = m_ue.bcPos(); + e.FPushClsMethodD(0, methName, m_curFunc->pce()->name()); + { FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart); } + e.FCall(0); + emitConvertToCell(e); + } else { + e.This(); + emitConstMethodCallNoParams(e, methName->toCppString()); + } emitSet(e); e.RetC(); diff --git a/hphp/compiler/analysis/emitter.h b/hphp/compiler/analysis/emitter.h index 56f18f9aeff..c4084e429fe 100644 --- a/hphp/compiler/analysis/emitter.h +++ b/hphp/compiler/analysis/emitter.h @@ -759,7 +759,8 @@ public: bool coerce_params = false); void emitMethodPrologue(Emitter& e, MethodStatementPtr meth); void emitMethod(MethodStatementPtr meth); - void emitMemoizeProp(Emitter &e, const StringData *propName); + void emitMemoizeProp(Emitter &e, MethodStatementPtr meth, + const StringData *propName); void emitMemoizeMethod(MethodStatementPtr meth, const StringData *methName, const StringData *propName); void emitConstMethodCallNoParams(Emitter& e, string name); diff --git a/hphp/test/quick/memoize_static.php b/hphp/test/quick/memoize_static.php index b20dac71fbd..2c86c80da15 100644 --- a/hphp/test/quick/memoize_static.php +++ b/hphp/test/quick/memoize_static.php @@ -1,7 +1,11 @@ > - public static function testStatic() { static $i = 100; return $i++; } + public static function testStatic() { static $i = 110; return $i++; } } -echo A::testStatic(); +echo A::testNotMemoized().' '; +echo A::testNotMemoized().' '; +echo A::testStatic().' '; +echo A::testStatic()."\n"; diff --git a/hphp/test/quick/memoize_static.php.expect b/hphp/test/quick/memoize_static.php.expect new file mode 100644 index 00000000000..7c70d9e4281 --- /dev/null +++ b/hphp/test/quick/memoize_static.php.expect @@ -0,0 +1 @@ +100 101 110 110 diff --git a/hphp/test/quick/memoize_static.php.expectf b/hphp/test/quick/memoize_static.php.expectf deleted file mode 100644 index fca0c90b357..00000000000 --- a/hphp/test/quick/memoize_static.php.expectf +++ /dev/null @@ -1,2 +0,0 @@ - -Fatal error: <<__Memoize>> cannot be used on static methods yet in %s/test/quick/memoize_static.php on line 4 -- 2.11.4.GIT