Rename closures and generators
Closures and generators are really hard to reason about in backtraces. For generators, I took the exact name of the method and put ##$continuation##. I put it at the end so it reads exactly like a method name since in WWW we use them almost the same as the method itself. For closures, I named the class ##Closue$Class::method#num## where num is an optional autoincrementing number. I decided to stop prefixing the methods with ##0## which is breaking the reflection tests. This basically redoes D782498 and D750608 but it works in repo mode.
Esse commit está contido em:
@@ -14,10 +14,8 @@
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include "hphp/compiler/analysis/analysis_result.h"
|
||||
#include "hphp/compiler/analysis/class_scope.h"
|
||||
#include "hphp/compiler/analysis/analysis_result.h"
|
||||
#include "hphp/compiler/analysis/code_error.h"
|
||||
#include "hphp/compiler/analysis/constant_table.h"
|
||||
#include "hphp/compiler/analysis/file_scope.h"
|
||||
@@ -46,6 +44,9 @@
|
||||
#include "hphp/runtime/base/zend/zend_string.h"
|
||||
#include "hphp/util/util.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
using namespace HPHP;
|
||||
using std::map;
|
||||
|
||||
@@ -753,8 +754,9 @@ const string& ClassScope::getNewGeneratorName(
|
||||
if (mapIt != genRenameMap.end()) {
|
||||
return mapIt->second;
|
||||
}
|
||||
string newName = oldName + "_" +
|
||||
lexical_cast<string>(genFuncScope->getNewID());
|
||||
string newName = ParserBase::newContinuationName(
|
||||
oldName + "_" + lexical_cast<string>(genFuncScope->getNewID())
|
||||
);
|
||||
genRenameMap[oldName] = newName;
|
||||
return genRenameMap[oldName];
|
||||
}
|
||||
@@ -777,7 +779,7 @@ ClassScope::renameCreateContinuationCalls(AnalysisResultPtr ar,
|
||||
const string &oldGenName =
|
||||
dynamic_pointer_cast<ScalarExpression>((*params)[1])->getString();
|
||||
|
||||
MethodStatementPtr origGenStmt = importedMethods[oldGenName];
|
||||
MethodStatementPtr origGenStmt = importedMethods[Util::toLower(oldGenName)];
|
||||
assert(origGenStmt);
|
||||
|
||||
const string &newGenName = origGenStmt->getOriginalName();
|
||||
|
||||
@@ -1141,9 +1141,11 @@ void MetaInfoBuilder::setForUnit(UnitEmitter& target) const {
|
||||
free(meta);
|
||||
}
|
||||
|
||||
EmitterVisitor::EmittedClosures EmitterVisitor::s_emittedClosures;
|
||||
|
||||
EmitterVisitor::EmitterVisitor(UnitEmitter& ue)
|
||||
: m_ue(ue), m_curFunc(ue.getMain()), m_evalStackIsUnknown(false),
|
||||
m_actualStackHighWater(0), m_fdescHighWater(0), m_closureCounter(0) {
|
||||
m_actualStackHighWater(0), m_fdescHighWater(0) {
|
||||
m_prevOpcode = OpLowInvalid;
|
||||
m_evalStack.m_actualStackHighWaterPtr = &m_actualStackHighWater;
|
||||
m_evalStack.m_fdescHighWaterPtr = &m_fdescHighWater;
|
||||
@@ -3826,14 +3828,10 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
|
||||
}
|
||||
}
|
||||
|
||||
StringData* className = newClosureName();
|
||||
const static StringData* parentName =
|
||||
StringData::GetStaticString("Closure");
|
||||
const Location* sLoc = ce->getLocation().get();
|
||||
PreClassEmitter* pce = m_ue.newPreClassEmitter(
|
||||
className, PreClass::AlwaysHoistable);
|
||||
pce->init(sLoc->line0, sLoc->line1, m_ue.bcPos(),
|
||||
AttrUnique | AttrPersistent, parentName, nullptr);
|
||||
// The parser generated a unique name for the function,
|
||||
// use that for the class
|
||||
std::string clsName = ce->getClosureFunction()->getOriginalName();
|
||||
StringData* className = StringData::GetStaticString(clsName);
|
||||
|
||||
// We're still at the closure definition site. Emit code to instantiate
|
||||
// the new anonymous class, with the use variables as arguments.
|
||||
@@ -3841,9 +3839,35 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
|
||||
for (int i = 0; i < useCount; ++i) {
|
||||
emitBuiltinCallArg(e, (*valuesList)[i], i, useVars[i].second);
|
||||
}
|
||||
|
||||
if (Option::WholeProgram) {
|
||||
int my_id;
|
||||
{
|
||||
EmittedClosures::accessor acc;
|
||||
s_emittedClosures.insert(acc, className);
|
||||
my_id = ++acc->second;
|
||||
}
|
||||
if (my_id > 1) {
|
||||
// The closure was from a trait, so we need a unique name in the
|
||||
// implementing class
|
||||
className = StringData::GetStaticString(
|
||||
// _ is different from the #, which is used for many closures in
|
||||
// the same func in ParserBase::newClosureName
|
||||
className->toCPPString() + '_' + std::to_string(my_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
e.CreateCl(useCount, className);
|
||||
|
||||
// From here on out, we're just building metadata for the closure.
|
||||
// From here on out, we're creating a new class to hold the closure.
|
||||
const static StringData* parentName =
|
||||
StringData::GetStaticString("Closure");
|
||||
const Location* sLoc = ce->getLocation().get();
|
||||
PreClassEmitter* pce = m_ue.newPreClassEmitter(
|
||||
className, PreClass::AlwaysHoistable);
|
||||
pce->init(sLoc->line0, sLoc->line1, m_ue.bcPos(),
|
||||
AttrUnique | AttrPersistent, parentName, nullptr);
|
||||
|
||||
// Instance variables.
|
||||
TypedValue uninit;
|
||||
@@ -6754,30 +6778,6 @@ void EmitterVisitor::finishFunc(Emitter& e, FuncEmitter* fe) {
|
||||
e.getUnitEmitter().recordFunction(fe);
|
||||
}
|
||||
|
||||
StringData* EmitterVisitor::newClosureName() {
|
||||
std::ostringstream str;
|
||||
str << "Closure" << '$';
|
||||
if (m_curFunc->pce() != nullptr) {
|
||||
str << m_curFunc->pce()->name()->data();
|
||||
}
|
||||
str << '$';
|
||||
if (m_curFunc->isPseudoMain()) {
|
||||
str << "__pseudoMain";
|
||||
} else {
|
||||
str << m_curFunc->name()->data();
|
||||
}
|
||||
/*
|
||||
* Uniquify the name
|
||||
*/
|
||||
str << '$'
|
||||
<< std::hex
|
||||
<< m_curFunc->ue().md5().q[1] << m_curFunc->ue().md5().q[0]
|
||||
<< std::dec
|
||||
<< '$' << m_closureCounter++;
|
||||
|
||||
return StringData::GetStaticString(str.str());
|
||||
}
|
||||
|
||||
void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val) {
|
||||
assert(val->isScalar());
|
||||
tvVal.m_type = KindOfUninit;
|
||||
|
||||
@@ -521,7 +521,9 @@ private:
|
||||
hphp_hash_map<Offset, SymbolicStack> m_jumpTargetEvalStacks;
|
||||
int m_actualStackHighWater;
|
||||
int m_fdescHighWater;
|
||||
int m_closureCounter; // used to uniquify closures' mangled names
|
||||
typedef tbb::concurrent_hash_map<const StringData*, int,
|
||||
StringDataHashCompare> EmittedClosures;
|
||||
static EmittedClosures s_emittedClosures;
|
||||
std::deque<ControlTargets> m_controlTargets;
|
||||
std::deque<Funclet> m_funclets;
|
||||
std::deque<ExnHandlerRegion*> m_exnHandlers;
|
||||
@@ -655,7 +657,6 @@ public:
|
||||
void copyOverFPIRegions(FuncEmitter* fe);
|
||||
void saveMaxStackCells(FuncEmitter* fe);
|
||||
void finishFunc(Emitter& e, FuncEmitter* fe);
|
||||
StringData* newClosureName();
|
||||
|
||||
void initScalar(TypedValue& tvVal, ExpressionPtr val);
|
||||
bool requiresDeepInit(ExpressionPtr initExpr) const;
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <limits.h>
|
||||
#include "hphp/compiler/expression/scalar_expression.h"
|
||||
#include "hphp/util/parser/hphp.tab.hpp"
|
||||
#include "hphp/util/util.h"
|
||||
@@ -35,6 +32,10 @@
|
||||
#include "hphp/runtime/ext/ext_variable.h"
|
||||
#include "hphp/compiler/analysis/file_scope.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <limits.h>
|
||||
|
||||
using namespace HPHP;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -157,16 +158,21 @@ void ScalarExpression::analyzeProgram(AnalysisResultPtr ar) {
|
||||
if (b && b->is(BlockScope::ClassScope)) {
|
||||
m_translated += "::";
|
||||
}
|
||||
m_translated += func->getOriginalName();
|
||||
if (func->isClosure()) {
|
||||
m_translated += "{closure}";
|
||||
} else {
|
||||
m_translated += func->getOriginalName();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case T_FUNC_C:
|
||||
if (getFunctionScope()) {
|
||||
m_translated = getFunctionScope()->getOriginalName();
|
||||
if (m_translated[0] == '0') {
|
||||
if (FunctionScopePtr func = getFunctionScope()) {
|
||||
if (func->isClosure()) {
|
||||
m_translated = "{closure}";
|
||||
} else {
|
||||
m_translated = func->getOriginalName();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -113,7 +113,7 @@ using namespace HPHP::Compiler;
|
||||
|
||||
extern void prepare_generator(Parser *_p, Token &stmt, Token ¶ms);
|
||||
extern void create_generator(Parser *_p, Token &out, Token ¶ms,
|
||||
Token &name, const std::string &closureName,
|
||||
Token &name, const std::string &genName,
|
||||
const char *clsname, Token *modifiers,
|
||||
bool getArgs, Token &origGenFunc, bool isHhvm,
|
||||
Token *attr);
|
||||
@@ -825,14 +825,20 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
|
||||
|
||||
FunctionStatementPtr func;
|
||||
|
||||
string funcName = name->text();
|
||||
if (funcName.empty()) {
|
||||
funcName = newClosureName(m_clsName, m_containingFuncName);
|
||||
} else if (m_lambdaMode) {
|
||||
funcName += "{lambda}";
|
||||
}
|
||||
|
||||
if (funcContext.isGenerator) {
|
||||
AnonFuncKind fKind = name->text().empty() ?
|
||||
ContinuationFromClosure : Continuation;
|
||||
const string &closureName = getAnonFuncName(fKind);
|
||||
string genName = newContinuationName(funcName);
|
||||
|
||||
Token new_params;
|
||||
prepare_generator(this, stmt, new_params);
|
||||
|
||||
func = NEW_STMT(FunctionStatement, exp, ref->num(), closureName,
|
||||
func = NEW_STMT(FunctionStatement, exp, ref->num(), genName,
|
||||
dynamic_pointer_cast<ExpressionList>(new_params->exp),
|
||||
ret.typeAnnotationName(),
|
||||
dynamic_pointer_cast<StatementList>(stmt->stmt),
|
||||
@@ -857,7 +863,7 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
|
||||
// the MethodStatement it's building will get the docComment
|
||||
pushComment(comment);
|
||||
Token origGenFunc;
|
||||
create_generator(this, out, params, name, closureName, nullptr, nullptr,
|
||||
create_generator(this, out, params, name, genName, nullptr, nullptr,
|
||||
hasCallToGetArgs, origGenFunc,
|
||||
(!Option::WholeProgram || !Option::ParseTimeOpts),
|
||||
attr);
|
||||
@@ -870,15 +876,6 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
|
||||
}
|
||||
|
||||
} else {
|
||||
string funcName = name->text();
|
||||
if (funcName.empty()) {
|
||||
funcName = getAnonFuncName(Closure);
|
||||
} else if (m_lambdaMode) {
|
||||
string f;
|
||||
f += GetAnonPrefix(CreateFunction);
|
||||
funcName = f + "_" + funcName;
|
||||
}
|
||||
|
||||
ExpressionListPtr attrList;
|
||||
if (attr && attr->exp) {
|
||||
attrList = dynamic_pointer_cast<ExpressionList>(attr->exp);
|
||||
@@ -1131,12 +1128,23 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref,
|
||||
fixStaticVars();
|
||||
|
||||
MethodStatementPtr mth;
|
||||
|
||||
string funcName = name->text();
|
||||
if (funcName.empty()) {
|
||||
funcName = newClosureName(m_clsName, m_containingFuncName);
|
||||
}
|
||||
|
||||
if (funcContext.isGenerator) {
|
||||
const string &closureName = getAnonFuncName(ParserBase::Continuation);
|
||||
string genName = newContinuationName(funcName);
|
||||
if (m_inTrait) {
|
||||
// see traits/2067.php
|
||||
genName = newContinuationName(funcName + "@" + m_clsName);
|
||||
}
|
||||
|
||||
Token new_params;
|
||||
prepare_generator(this, stmt, new_params);
|
||||
ModifierExpressionPtr exp2 = Construct::Clone(exp);
|
||||
mth = NEW_STMT(MethodStatement, exp2, ref->num(), closureName,
|
||||
mth = NEW_STMT(MethodStatement, exp2, ref->num(), genName,
|
||||
dynamic_pointer_cast<ExpressionList>(new_params->exp),
|
||||
ret.typeAnnotationName(),
|
||||
dynamic_pointer_cast<StatementList>(stmt->stmt),
|
||||
@@ -1154,7 +1162,7 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref,
|
||||
// the MethodStatement it's building will get the docComment
|
||||
pushComment(comment);
|
||||
Token origGenFunc;
|
||||
create_generator(this, out, params, name, closureName, m_clsName.c_str(),
|
||||
create_generator(this, out, params, name, genName, m_clsName.c_str(),
|
||||
&modifiers, hasCallToGetArgs, origGenFunc,
|
||||
(!Option::WholeProgram || !Option::ParseTimeOpts),
|
||||
attr);
|
||||
@@ -1169,7 +1177,7 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref,
|
||||
if (attr && attr->exp) {
|
||||
attrList = dynamic_pointer_cast<ExpressionList>(attr->exp);
|
||||
}
|
||||
mth = NEW_STMT(MethodStatement, exp, ref->num(), name->text(),
|
||||
mth = NEW_STMT(MethodStatement, exp, ref->num(), funcName,
|
||||
old_params,
|
||||
ret.typeAnnotationName(),
|
||||
stmts, attribute, comment,
|
||||
@@ -1556,6 +1564,11 @@ void Parser::onThrow(Token &out, Token &expr) {
|
||||
}
|
||||
|
||||
void Parser::onClosureStart(Token &name) {
|
||||
if (!m_funcName.empty()) {
|
||||
m_containingFuncName = m_funcName;
|
||||
} else {
|
||||
// pseudoMain
|
||||
}
|
||||
onFunctionStart(name, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -304,6 +304,7 @@ private:
|
||||
std::vector<ScalarExpressionPtr> m_compilerHaltOffsetVec;
|
||||
std::string m_clsName; // for T_CLASS_C inside a closure
|
||||
std::string m_funcName;
|
||||
std::string m_containingFuncName;
|
||||
bool m_inTrait;
|
||||
|
||||
// parser output
|
||||
|
||||
@@ -1455,8 +1455,7 @@ bool f_fb_rename_function(CStrRef orig_func_name, CStrRef new_func_name) {
|
||||
}
|
||||
|
||||
if (function_exists(new_func_name)) {
|
||||
if (new_func_name.data()[0] !=
|
||||
ParserBase::CharCreateFunction) { // create_function
|
||||
if (new_func_name.data()[0] != '1') {
|
||||
raise_warning("fb_rename_function(%s, %s) failed: %s already exists!",
|
||||
orig_func_name.data(), new_func_name.data(),
|
||||
new_func_name.data());
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "hphp/runtime/base/runtime_option.h"
|
||||
#include "hphp/runtime/base/string_util.h"
|
||||
#include "hphp/runtime/vm/translator/translator-inline.h"
|
||||
#include "hphp/util/parser/parser.h"
|
||||
|
||||
#include "hphp/system/lib/systemlib.h"
|
||||
|
||||
@@ -745,7 +746,10 @@ Array f_hphp_get_class_info(CVarRef name) {
|
||||
size_t const numMethods = cls->preClass()->numMethods();
|
||||
for (Slot i = 0; i < numMethods; ++i) {
|
||||
const Func* m = methods[i];
|
||||
if (isdigit(m->name()->data()[0])) continue;
|
||||
if (isdigit(m->name()->data()[0]) ||
|
||||
ParserBase::IsClosureOrContinuationName(m->name()->toCPPString())) {
|
||||
continue;
|
||||
}
|
||||
Array info = Array::Create();
|
||||
set_method_info(info, m);
|
||||
arr.set(StringUtil::ToLower(m->nameRef()), VarNR(info));
|
||||
@@ -756,7 +760,10 @@ Array f_hphp_get_class_info(CVarRef name) {
|
||||
i < cls->traitsEndIdx();
|
||||
++i) {
|
||||
const Func* m = clsMethods[i];
|
||||
if (isdigit(m->name()->data()[0])) continue;
|
||||
if (isdigit(m->name()->data()[0]) ||
|
||||
ParserBase::IsClosureOrContinuationName(m->name()->toCPPString())) {
|
||||
continue;
|
||||
}
|
||||
Array info = Array::Create();
|
||||
set_method_info(info, m);
|
||||
arr.set(StringUtil::ToLower(m->nameRef()), VarNR(info));
|
||||
|
||||
@@ -574,7 +574,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(5) "enter"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
array(1) {
|
||||
["args"]=>
|
||||
@@ -589,7 +589,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(4) "exit"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
@@ -674,7 +674,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(5) "enter"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
array(1) {
|
||||
["args"]=>
|
||||
@@ -689,7 +689,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(4) "exit"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
@@ -766,7 +766,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(5) "enter"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
array(1) {
|
||||
["args"]=>
|
||||
@@ -781,7 +781,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(4) "exit"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
@@ -823,7 +823,7 @@ array(3) {
|
||||
[0]=>
|
||||
string(4) "exit"
|
||||
[1]=>
|
||||
string(%d) "%s"
|
||||
string(16) "gen$continuation"
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
class A {
|
||||
public function gen() {
|
||||
public function Gen() {
|
||||
var_dump($this);
|
||||
yield 1; yield 2; yield 3;
|
||||
}
|
||||
|
||||
public static function sgen() {
|
||||
public static function SGen() {
|
||||
var_dump(get_called_class());
|
||||
yield 4; yield 5; yield 6;
|
||||
}
|
||||
@@ -14,5 +14,5 @@ class A {
|
||||
|
||||
|
||||
$a = new A();
|
||||
foreach ($a->gen() as $num) { var_dump($num); }
|
||||
foreach (A::sgen() as $num) { var_dump($num); }
|
||||
foreach ($a->Gen() as $num) { var_dump($num); }
|
||||
foreach (A::SGen() as $num) { var_dump($num); }
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
trait A {
|
||||
function b() {
|
||||
$c = function() {
|
||||
return 'd';
|
||||
};
|
||||
var_dump($c);
|
||||
return $c();
|
||||
}
|
||||
}
|
||||
|
||||
class E { use A; }
|
||||
class F { use A; }
|
||||
|
||||
function main() {
|
||||
var_dump((new E)->b());
|
||||
var_dump((new F)->b());
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,6 @@
|
||||
object(Closure$A::b%S)#2 (0) {
|
||||
}
|
||||
string(1) "d"
|
||||
object(Closure$A::b%S)#3 (0) {
|
||||
}
|
||||
string(1) "d"
|
||||
@@ -1 +1 @@
|
||||
HipHop Fatal error: Cannot override final method A::method1() in %s on line 4
|
||||
HipHop Fatal error: Cannot override final method A::%Smethod1%S() in %s on line 4
|
||||
|
||||
@@ -2,8 +2,8 @@ object(foo)#1 (2) {
|
||||
["test":"foo":private]=>
|
||||
int(3)
|
||||
["a"]=>
|
||||
object(Closure$foo$x$3f94cf6423ec56252c9aba668f708bce$0)#2 (1) {
|
||||
["a":"Closure$foo$x$3f94cf6423ec56252c9aba668f708bce$0":private]=>
|
||||
object(Closure$foo::x)#2 (1) {
|
||||
["a":"Closure$foo::x":private]=>
|
||||
*RECURSION*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ bool TestParser::VerifyParser(const char *input, const char *output,
|
||||
bool ret = true;
|
||||
{
|
||||
AnalysisResultPtr ar(new AnalysisResult());
|
||||
Compiler::Parser::Reset();
|
||||
StatementListPtr tree = Compiler::Parser::ParseString(input, ar);
|
||||
std::ostringstream code;
|
||||
CodeGenerator cg(&code);
|
||||
|
||||
@@ -666,7 +666,7 @@ bool TestParserStmt::TestYieldStatement() {
|
||||
"}\n"
|
||||
"function foo() {\n"
|
||||
"return hphp_create_continuation"
|
||||
"('', '3990978909_1', __FUNCTION__);\n"
|
||||
"('', 'foo$continuation', __FUNCTION__);\n"
|
||||
"}\n");
|
||||
|
||||
V("<?php function foo() { yield 123;}",
|
||||
@@ -676,14 +676,14 @@ bool TestParserStmt::TestYieldStatement() {
|
||||
"}\n"
|
||||
"function foo() {\n"
|
||||
"return hphp_create_continuation"
|
||||
"('', '3990978909_1', __FUNCTION__);\n"
|
||||
"('', 'foo$continuation', __FUNCTION__);\n"
|
||||
"}\n");
|
||||
|
||||
V("<?php class bar { function foo() { yield 123; yield 456;} }",
|
||||
"class bar {\n"
|
||||
"public function foo() {\n"
|
||||
"return hphp_create_continuation"
|
||||
"(__CLASS__, '3990978909_1', __METHOD__);\n"
|
||||
"(__CLASS__, 'foo$continuation', __METHOD__);\n"
|
||||
"}\n"
|
||||
"public function ($" CONTINUATION_OBJECT_NAME ") {\n"
|
||||
"hphp_unpack_continuation();\n"
|
||||
|
||||
@@ -16,58 +16,50 @@
|
||||
|
||||
#include "parser.h"
|
||||
#include "hphp/util/hash.h"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Mutex ParserBase::s_mutex;
|
||||
std::map<int64_t, int> ParserBase::s_closureIds;
|
||||
|
||||
char ParserBase::GetAnonPrefix(AnonFuncKind kind) {
|
||||
static_assert(Closure == 0 && Continuation <= 9,
|
||||
"AnonFuncKind enum has unexpected values");
|
||||
static_assert(CharClosure == '0' && CharContinuation <= '9',
|
||||
"AnonFuncKindChar enum has unexpected values");
|
||||
return '0' + kind;
|
||||
}
|
||||
|
||||
template <int i>
|
||||
static bool NameImpl(const std::string &name) {
|
||||
return !name.empty() && isdigit(name[0]) && i == (name[0] - '0');
|
||||
}
|
||||
|
||||
bool ParserBase::IsClosureName(const std::string &name) {
|
||||
return NameImpl<Closure>(name);
|
||||
}
|
||||
|
||||
bool ParserBase::IsCreateFunctionName(const std::string &name) {
|
||||
return NameImpl<CreateFunction>(name);
|
||||
return boost::istarts_with(name, "closure$");
|
||||
}
|
||||
|
||||
bool ParserBase::IsContinuationName(const std::string &name) {
|
||||
return NameImpl<ContinuationFromClosure>(name) ||
|
||||
NameImpl<Continuation>(name);
|
||||
}
|
||||
|
||||
bool ParserBase::IsContinuationFromClosureName(const std::string &name) {
|
||||
return NameImpl<ContinuationFromClosure>(name);
|
||||
return boost::iends_with(name, "$continuation");
|
||||
}
|
||||
|
||||
bool ParserBase::IsClosureOrContinuationName(const std::string &name) {
|
||||
return IsClosureName(name) || IsContinuationName(name);
|
||||
}
|
||||
|
||||
bool ParserBase::IsAnonFunctionName(const char *name) {
|
||||
if (!*name) return true;
|
||||
char begin = CharClosure;
|
||||
char end = CharContinuation;
|
||||
char test = name[0];
|
||||
return begin <= test && test <= end;
|
||||
std::string ParserBase::newContinuationName(const std::string &name) {
|
||||
assert(!name.empty());
|
||||
size_t pos = 0;
|
||||
std::string shorterName = name;
|
||||
std::string suffix("$continuation");
|
||||
while((pos = shorterName.find(suffix, pos)) != std::string::npos) {
|
||||
shorterName.replace(pos, suffix.length(), "");
|
||||
}
|
||||
return shorterName + suffix;
|
||||
}
|
||||
|
||||
void ParserBase::Reset() {
|
||||
Lock lock(s_mutex);
|
||||
s_closureIds.clear();
|
||||
std::string ParserBase::newClosureName(
|
||||
const std::string &className,
|
||||
const std::string &funcName) {
|
||||
std::string name = "Closure$";
|
||||
if (!className.empty()) {
|
||||
name += className + "::";
|
||||
}
|
||||
name += funcName;
|
||||
|
||||
static std::map<std::string, int> s_seenClosures;
|
||||
int id = ++s_seenClosures[name];
|
||||
if (id > 1) {
|
||||
// we've seen the same name before, uniquify
|
||||
name = name + '#' + std::to_string(id);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -145,23 +137,6 @@ void ParserBase::popClass() {
|
||||
m_classes.pop_back();
|
||||
}
|
||||
|
||||
std::string ParserBase::getAnonFuncName(AnonFuncKind kind) {
|
||||
int64_t h = hash_string_cs(m_fileName, strlen(m_fileName));
|
||||
int closureId;
|
||||
{
|
||||
Lock lock(s_mutex);
|
||||
int &id = s_closureIds[h];
|
||||
closureId = ++id;
|
||||
}
|
||||
|
||||
string ret;
|
||||
ret += GetAnonPrefix(kind);
|
||||
ret += boost::lexical_cast<string>(h);
|
||||
ret += "_";
|
||||
ret += boost::lexical_cast<string>(closureId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// typevar scopes
|
||||
|
||||
|
||||
+15
-34
@@ -57,39 +57,13 @@ public:
|
||||
StaticName
|
||||
};
|
||||
|
||||
/**
|
||||
* These numbers are scattered throughout the code base (often hardcoded).
|
||||
* Do not change unless you change all the occurances.
|
||||
*/
|
||||
enum AnonFuncKind {
|
||||
Closure,
|
||||
CreateFunction,
|
||||
ContinuationFromClosure,
|
||||
Continuation
|
||||
};
|
||||
|
||||
enum AnonFuncKindChar {
|
||||
CharClosure = '0',
|
||||
CharCreateFunction,
|
||||
CharContinuationFromClosure,
|
||||
CharContinuation
|
||||
};
|
||||
|
||||
static char GetAnonPrefix(AnonFuncKind kind);
|
||||
|
||||
static bool IsClosureName (const std::string &name);
|
||||
static bool IsCreateFunctionName (const std::string &name);
|
||||
static bool IsContinuationName (const std::string &name);
|
||||
static bool IsContinuationFromClosureName(const std::string &name);
|
||||
static bool IsClosureOrContinuationName (const std::string &name);
|
||||
static bool IsAnonFunctionName (const std::string &name) {
|
||||
return IsAnonFunctionName(name.c_str());
|
||||
}
|
||||
static bool IsAnonFunctionName (const char *name);
|
||||
/**
|
||||
* Reset parser static variables. Good for unit tests.
|
||||
*/
|
||||
static void Reset();
|
||||
static std::string newContinuationName (const std::string &name);
|
||||
std::string newClosureName(
|
||||
const std::string &className,
|
||||
const std::string &funcName);
|
||||
|
||||
public:
|
||||
ParserBase(Scanner &scanner, const char *fileName);
|
||||
@@ -145,7 +119,6 @@ public:
|
||||
void pushClass(bool isXhpClass);
|
||||
bool peekClass();
|
||||
void popClass();
|
||||
std::string getAnonFuncName(AnonFuncKind kind);
|
||||
|
||||
// for typevar checking
|
||||
void pushTypeScope();
|
||||
@@ -215,9 +188,17 @@ protected:
|
||||
};
|
||||
std::vector<LabelInfo> m_labelInfos; // stack by function
|
||||
|
||||
// for closure hidden name
|
||||
static Mutex s_mutex;
|
||||
static std::map<int64_t, int> s_closureIds;
|
||||
// for namespace support
|
||||
enum NamespaceState {
|
||||
SeenNothing,
|
||||
SeenNonNamespaceStatement,
|
||||
SeenNamespaceStatement,
|
||||
InsideNamespace,
|
||||
};
|
||||
NamespaceState m_nsState;
|
||||
bool m_nsFileScope;
|
||||
std::string m_namespace; // current namespace
|
||||
hphp_string_imap<std::string> m_aliases;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário