allow static keyword before closure
PHP added this in 5.4 so that you can say your closure shouldn't capture ##$this##. https://wiki.php.net/rfc/closures Should we add it? Many of their unit tests use it. Instead of putting a boolean somewhere I used the same attr framework and set the static bit there. Thoughts? It only needs one change in the ##FPushFunc##.
Esse commit está contido em:
@@ -178,6 +178,7 @@ public:
|
||||
VariableTablePtr getVariables() { return m_variables;}
|
||||
ConstantTablePtr getConstants() { return m_constants;}
|
||||
ClassScopeRawPtr getContainingClass();
|
||||
FunctionScopeRawPtr getContainingNonClosureFunction();
|
||||
FunctionScopeRawPtr getContainingFunction() const {
|
||||
return FunctionScopeRawPtr(is(FunctionScope) ?
|
||||
(HPHP::FunctionScope*)this : 0);
|
||||
@@ -341,8 +342,6 @@ protected:
|
||||
int m_updated;
|
||||
int m_runId;
|
||||
private:
|
||||
FunctionScopeRawPtr getContainingNonClosureFunction();
|
||||
|
||||
Marks m_mark;
|
||||
BlockScopeRawPtrFlagsPtrVec m_orderedDeps;
|
||||
BlockScopeRawPtrFlagsVec m_orderedUsers;
|
||||
|
||||
@@ -396,6 +396,7 @@ FunctionScopePtr FileScope::createPseudoMain(AnalysisResultConstPtr ar) {
|
||||
StatementListPtr st = m_tree;
|
||||
FunctionStatementPtr f
|
||||
(new FunctionStatement(BlockScopePtr(), LocationPtr(),
|
||||
ModifierExpressionPtr(),
|
||||
false, pseudoMainName(),
|
||||
ExpressionListPtr(), st, 0, "",
|
||||
ExpressionListPtr()));
|
||||
|
||||
@@ -331,6 +331,11 @@ bool FunctionScope::needsActRec() const {
|
||||
return res;
|
||||
}
|
||||
|
||||
bool FunctionScope::mayContainThis() {
|
||||
return inPseudoMain() || getContainingClass() ||
|
||||
(isClosure() && !m_modifiers->isStatic());
|
||||
}
|
||||
|
||||
bool FunctionScope::isClosure() const {
|
||||
return ParserBase::IsClosureName(name());
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ public:
|
||||
bool hasImpl() const;
|
||||
void setDirectInvoke() { m_directInvoke = true; }
|
||||
bool hasDirectInvoke() const { return m_directInvoke; }
|
||||
bool mayContainThis();
|
||||
bool isClosure() const;
|
||||
bool isGenerator() const;
|
||||
bool isGeneratorFromClosure() const;
|
||||
|
||||
@@ -172,6 +172,13 @@ void ClosureExpression::analyzeProgram(AnalysisResultPtr ar) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FunctionScopeRawPtr container =
|
||||
getFunctionScope()->getContainingNonClosureFunction();
|
||||
if (container && container->isStatic()) {
|
||||
m_func->getModifiers()->add(T_STATIC);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TypePtr ClosureExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
|
||||
|
||||
@@ -146,7 +146,7 @@ void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) {
|
||||
|
||||
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
|
||||
if (FunctionScopePtr func = getFunctionScope()) {
|
||||
if (m_name == "this" && (func->inPseudoMain() || getClassScope())) {
|
||||
if (m_name == "this" && func->mayContainThis()) {
|
||||
func->setContainsThis();
|
||||
m_this = true;
|
||||
if (!hasContext(ObjectContext)) {
|
||||
|
||||
+10637
-10389
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+3242
-3209
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -789,8 +789,13 @@ void Parser::fixStaticVars() {
|
||||
m_staticVars.pop_back();
|
||||
}
|
||||
|
||||
void Parser::onFunction(Token &out, Token &ret, Token &ref, Token &name,
|
||||
Token ¶ms, Token &stmt, Token *attr) {
|
||||
void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
|
||||
Token &name, Token ¶ms, Token &stmt, Token *attr) {
|
||||
|
||||
ModifierExpressionPtr exp = modifiers?
|
||||
dynamic_pointer_cast<ModifierExpression>(modifiers->exp)
|
||||
: NEW_EXP0(ModifierExpression);
|
||||
|
||||
const string &retType = ret.text();
|
||||
if (!retType.empty() && !ret.check()) {
|
||||
PARSE_ERROR("Return type hint is not supported yet");
|
||||
@@ -825,7 +830,7 @@ void Parser::onFunction(Token &out, Token &ret, Token &ref, Token &name,
|
||||
Token new_params;
|
||||
prepare_generator(this, stmt, new_params, funcContext.numYields);
|
||||
|
||||
func = NEW_STMT(FunctionStatement, ref->num(), closureName,
|
||||
func = NEW_STMT(FunctionStatement, exp, ref->num(), closureName,
|
||||
dynamic_pointer_cast<ExpressionList>(new_params->exp),
|
||||
dynamic_pointer_cast<StatementList>(stmt->stmt),
|
||||
attribute, comment, ExpressionListPtr());
|
||||
@@ -876,7 +881,7 @@ void Parser::onFunction(Token &out, Token &ret, Token &ref, Token &name,
|
||||
attrList = dynamic_pointer_cast<ExpressionList>(attr->exp);
|
||||
}
|
||||
|
||||
func = NEW_STMT(FunctionStatement, ref->num(), funcName,
|
||||
func = NEW_STMT(FunctionStatement, exp, ref->num(), funcName,
|
||||
old_params, dynamic_pointer_cast<StatementList>(stmt->stmt),
|
||||
attribute, comment, attrList);
|
||||
out->stmt = func;
|
||||
@@ -1540,10 +1545,16 @@ void Parser::onClosureStart(Token &name) {
|
||||
onFunctionStart(name, true);
|
||||
}
|
||||
|
||||
void Parser::onClosure(Token &out, Token &ret, Token &ref, Token ¶ms,
|
||||
Token &cparams, Token &stmts) {
|
||||
Token func, name;
|
||||
onFunction(func, ret, ref, name, params, stmts, 0);
|
||||
void Parser::onClosure(Token &out, Token &ret, Token &ref, Token ¶ms,
|
||||
Token &cparams, Token &stmts, bool is_static) {
|
||||
Token func, name, modifiers;
|
||||
|
||||
ModifierExpressionPtr modifier_exp = NEW_EXP0(ModifierExpression);
|
||||
modifiers->exp = modifier_exp;
|
||||
if (is_static) {
|
||||
modifier_exp->add(T_STATIC);
|
||||
}
|
||||
onFunction(func, &modifiers, ret, ref, name, params, stmts, 0);
|
||||
|
||||
out.reset();
|
||||
out->exp = NEW_EXP(ClosureExpression,
|
||||
|
||||
@@ -167,8 +167,8 @@ public:
|
||||
void onClassConst(Token &out, Token &cls, Token &name, bool text);
|
||||
void fixStaticVars();
|
||||
void onFunctionStart(Token &name, bool doPushComment = true);
|
||||
void onFunction(Token &out, Token &ret, Token &ref, Token &name,
|
||||
Token ¶ms, Token &stmt, Token *attr);
|
||||
void onFunction(Token &out, Token *modifier, Token &ret, Token &ref,
|
||||
Token &name, Token ¶ms, Token &stmt, Token *attr);
|
||||
void onParam(Token &out, Token *params, Token &type, Token &var,
|
||||
bool ref, Token *defValue, Token *attr);
|
||||
void onClassStart(int type, Token &name);
|
||||
@@ -230,7 +230,7 @@ public:
|
||||
|
||||
void onClosureStart(Token &name);
|
||||
void onClosure(Token &out, Token &ret, Token &ref, Token ¶ms,
|
||||
Token &cparams, Token &stmts);
|
||||
Token &cparams, Token &stmts, bool is_static);
|
||||
void onClosureParam(Token &out, Token *params, Token ¶m, bool ref);
|
||||
void onLabel(Token &out, Token &label);
|
||||
void onGoto(Token &out, Token &label, bool limited);
|
||||
|
||||
@@ -35,11 +35,11 @@ using namespace HPHP;
|
||||
|
||||
FunctionStatement::FunctionStatement
|
||||
(STATEMENT_CONSTRUCTOR_PARAMETERS,
|
||||
bool ref, const std::string &name, ExpressionListPtr params,
|
||||
StatementListPtr stmt, int attr, const std::string &docComment,
|
||||
ExpressionListPtr attrList)
|
||||
ModifierExpressionPtr modifiers, bool ref, const std::string &name,
|
||||
ExpressionListPtr params, StatementListPtr stmt, int attr, const
|
||||
std::string &docComment, ExpressionListPtr attrList)
|
||||
: MethodStatement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(FunctionStatement),
|
||||
ModifierExpressionPtr(), ref, name, params, stmt, attr,
|
||||
modifiers, ref, name, params, stmt, attr,
|
||||
docComment, attrList, false), m_ignored(false) {
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ DECLARE_BOOST_TYPES(FunctionStatement);
|
||||
class FunctionStatement : public MethodStatement {
|
||||
public:
|
||||
FunctionStatement(STATEMENT_CONSTRUCTOR_PARAMETERS,
|
||||
bool ref, const std::string &name,
|
||||
ExpressionListPtr params, StatementListPtr stmt,
|
||||
int attr,
|
||||
ModifierExpressionPtr modifiers, bool ref,
|
||||
const std::string &name, ExpressionListPtr params,
|
||||
StatementListPtr stmt, int attr,
|
||||
const std::string &docComment,
|
||||
ExpressionListPtr attrList);
|
||||
|
||||
|
||||
@@ -41,18 +41,25 @@ void c_Closure::t___construct() {
|
||||
VM::ActRec* childClosure = context->getPrevVMState(me);
|
||||
ar = context->getPrevVMState(childClosure);
|
||||
}
|
||||
|
||||
static StringData* invokeName = StringData::GetStaticString("__invoke");
|
||||
VM::Func* invokeFunc = getVMClass()->lookupMethod(invokeName);
|
||||
|
||||
// I don't care if it is a $this or a late bound class because we will just
|
||||
// put it back in the same place on an ActRec.
|
||||
m_thisOrClass = ar->m_this;
|
||||
if (ar->hasThis()) {
|
||||
ar->getThis()->incRefCount();
|
||||
if (invokeFunc->attrs() & VM::AttrStatic) {
|
||||
// Only set the class for static closures
|
||||
m_thisOrClass = (ObjectData*)(intptr_t(ar->m_func->cls()) | 1LL);
|
||||
} else {
|
||||
// I don't care if it is a $this or a late bound class because we will just
|
||||
// put it back in the same place on an ActRec.
|
||||
m_thisOrClass = ar->m_this;
|
||||
if (ar->hasThis()) {
|
||||
ar->getThis()->incRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Change my __invoke's m_cls to be the same as my creator's
|
||||
static StringData* invokeName = StringData::GetStaticString("__invoke");
|
||||
VM::Class* scope = ar->m_func->cls();
|
||||
m_func = getVMClass()->lookupMethod(invokeName)->cloneAndSetClass(scope);
|
||||
m_func = invokeFunc->cloneAndSetClass(scope);
|
||||
}
|
||||
|
||||
ObjectData* c_Closure::clone() {
|
||||
|
||||
@@ -5582,7 +5582,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFPushFunc(PC& pc) {
|
||||
ar->m_func = func;
|
||||
arSetSfp(ar, m_fp);
|
||||
if (origObj) {
|
||||
if (func->attrs() & AttrStatic) {
|
||||
if (func->attrs() & AttrStatic && !func->isClosureBody()) {
|
||||
ar->setClass(origObj->getVMClass());
|
||||
decRefObj(origObj);
|
||||
} else {
|
||||
|
||||
@@ -1160,7 +1160,8 @@ void
|
||||
TranslatorX64::irTranslateThis(const Tracelet &t,
|
||||
const NormalizedInstruction &i) {
|
||||
assert(i.outStack && !i.outLocal);
|
||||
assert(curFunc()->isPseudoMain() || curFunc()->cls());
|
||||
assert(curFunc()->isPseudoMain() || curFunc()->cls() ||
|
||||
curFunc()->isClosureBody());
|
||||
|
||||
HHIR_EMIT(This);
|
||||
}
|
||||
@@ -1169,7 +1170,8 @@ void
|
||||
TranslatorX64::irTranslateBareThis(const Tracelet &t,
|
||||
const NormalizedInstruction &i) {
|
||||
assert(i.outStack && !i.outLocal);
|
||||
assert(curFunc()->isPseudoMain() || curFunc()->cls());
|
||||
assert(curFunc()->isPseudoMain() || curFunc()->cls() ||
|
||||
curFunc()->isClosureBody());
|
||||
|
||||
HHIR_EMIT(BareThis, (i.imm[0].u_OA));
|
||||
}
|
||||
|
||||
@@ -9377,7 +9377,8 @@ TranslatorX64::translateThis(const Tracelet &t,
|
||||
}
|
||||
|
||||
assert(!i.outLocal);
|
||||
assert(curFunc()->isPseudoMain() || curFunc()->cls());
|
||||
assert(curFunc()->isPseudoMain() || curFunc()->cls() ||
|
||||
curFunc()->isClosureBody());
|
||||
m_regMap.allocOutputRegs(i);
|
||||
PhysReg out = getReg(i.outStack->location);
|
||||
a. load_reg64_disp_reg64(rVmFp, AROFF(m_this), out);
|
||||
@@ -9396,7 +9397,7 @@ TranslatorX64::translateBareThis(const Tracelet &t,
|
||||
return;
|
||||
}
|
||||
assert(!i.outLocal);
|
||||
assert(curFunc()->cls());
|
||||
assert(curFunc()->cls() || curFunc()->isClosureBody());
|
||||
ScratchReg outScratch(m_regMap);
|
||||
PhysReg out = r(outScratch);
|
||||
PhysReg base;
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_recursive_bad.php on line 6
|
||||
HipHop Fatal error: Uncaught exception 'BadMethodCallException' with message 'Call to a member function d() on a non-object' in hphp/test/vm/closure_recursive_bad.php:6\nStack trace:\n#0 hphp/test/vm/closure_recursive_bad.php(17): {closure}()\n#1 {main}
|
||||
HipHop Fatal error: $this is null in hphp/test/vm/closure_recursive_bad.php on line 6
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
class A {
|
||||
private $c = 1;
|
||||
|
||||
function b() {
|
||||
$a = function () {
|
||||
var_dump($this);
|
||||
};
|
||||
$a();
|
||||
|
||||
$a = static function () {
|
||||
var_dump($this);
|
||||
};
|
||||
$a();
|
||||
}
|
||||
|
||||
static function c() {
|
||||
$a = function () {
|
||||
var_dump($this);
|
||||
};
|
||||
$a();
|
||||
|
||||
$a = static function () {
|
||||
var_dump($this);
|
||||
};
|
||||
$a();
|
||||
}
|
||||
}
|
||||
|
||||
(new A)->b();
|
||||
(new A)->c();
|
||||
@@ -0,0 +1,10 @@
|
||||
object(A)#1 (1) {
|
||||
["c":"A":private]=>
|
||||
int(1)
|
||||
}
|
||||
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 13
|
||||
NULL
|
||||
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 20
|
||||
NULL
|
||||
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 25
|
||||
NULL
|
||||
Link simbólico
+1
@@ -0,0 +1 @@
|
||||
filepath.filter
|
||||
@@ -310,7 +310,7 @@ void create_generator(Parser *_p, Token &out, Token ¶ms,
|
||||
_p->finishStatement(out, stmts2); out = 1;
|
||||
} else {
|
||||
out.reset();
|
||||
_p->onFunction(out, ret, ref, name, params, scont, attr);
|
||||
_p->onFunction(out, modifiers, ret, ref, name, params, scont, attr);
|
||||
origGenFunc = out;
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1139,7 @@ function_declaration_statement:
|
||||
'(' parameter_list ')'
|
||||
sm_opt_return_type
|
||||
'{' inner_statement_list '}' { Token t; t.reset();
|
||||
_p->onFunction($$,t,$2,$3,$6,$10,0);
|
||||
_p->onFunction($$,0,t,$2,$3,$6,$10,0);
|
||||
_p->popLabelInfo();
|
||||
_p->popTypeScope();}
|
||||
| non_empty_user_attributes function_loc
|
||||
@@ -1149,7 +1149,7 @@ function_declaration_statement:
|
||||
'(' parameter_list ')'
|
||||
sm_opt_return_type
|
||||
'{' inner_statement_list '}' { Token t; t.reset();
|
||||
_p->onFunction($$,t,$3,$4,$7,$11,&$1);
|
||||
_p->onFunction($$,0,t,$3,$4,$7,$11,&$1);
|
||||
_p->popLabelInfo();
|
||||
_p->popTypeScope();}
|
||||
;
|
||||
@@ -1717,12 +1717,21 @@ expr_no_variable:
|
||||
| array_literal { $$ = $1;}
|
||||
| '`' backticks_expr '`' { _p->onEncapsList($$,'`',$2);}
|
||||
| T_PRINT expr { UEXP($$,$2,T_PRINT,1);}
|
||||
| function_loc is_reference '(' { Token t; _p->onClosureStart(t);
|
||||
| function_loc
|
||||
is_reference '(' { Token t; _p->onClosureStart(t);
|
||||
_p->pushLabelInfo();}
|
||||
parameter_list ')'
|
||||
sm_opt_return_type lexical_vars
|
||||
'{' inner_statement_list '}' { Token u; u.reset();
|
||||
_p->onClosure($$,u,$2,$5,$8,$10);
|
||||
_p->onClosure($$,u,$2,$5,$8,$10,0);
|
||||
_p->popLabelInfo();}
|
||||
| T_STATIC function_loc
|
||||
is_reference '(' { Token t; _p->onClosureStart(t);
|
||||
_p->pushLabelInfo();}
|
||||
parameter_list ')'
|
||||
sm_opt_return_type lexical_vars
|
||||
'{' inner_statement_list '}' { Token u; u.reset();
|
||||
_p->onClosure($$,u,$3,$6,$9,$11,1);
|
||||
_p->popLabelInfo();}
|
||||
| xhp_tag { $$ = $1;}
|
||||
| dim_expr { $$ = $1;}
|
||||
|
||||
+10637
-10389
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+3221
-3192
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -316,9 +316,9 @@ struct Parser : ParserBase {
|
||||
X(name);
|
||||
}
|
||||
|
||||
void onFunction(Token& out, Token& ret, Token& ref, Token& name,
|
||||
Token& params, Token& stmt, Token* attr) {
|
||||
X(ret, ref, name, params, stmt, attr);
|
||||
void onFunction(Token& out, Token* modifiers, Token& ret, Token& ref,
|
||||
Token& name, Token& params, Token& stmt, Token* attr) {
|
||||
X(modifiers, ret, ref, name, params, stmt, attr);
|
||||
}
|
||||
|
||||
void onParam(Token &out, Token *params, Token &type, Token &var,
|
||||
@@ -482,9 +482,9 @@ struct Parser : ParserBase {
|
||||
|
||||
void onThrow(Token &out, Token &expr) { X(expr); }
|
||||
|
||||
void onClosure(Token &out, Token &ret, Token &ref, Token ¶ms,
|
||||
Token &cparams, Token &stmts) {
|
||||
X(ret, ref, params, cparams, stmts);
|
||||
void onClosure(Token &out, Token &ret, Token &ref, Token ¶ms,
|
||||
Token &cparams, Token &stmts, bool is_static) {
|
||||
X(ret, ref, params, cparams, stmts, is_static);
|
||||
}
|
||||
|
||||
void onClosureParam(Token &out, Token *params, Token ¶m, bool ref) {
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário