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:
ptarjan
2013-03-20 15:31:37 -07:00
commit de Sara Golemon
commit a1802db1d2
24 arquivos alterados com 27868 adições e 27225 exclusões
+1 -2
Ver Arquivo
@@ -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;
+1
Ver Arquivo
@@ -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()));
+5
Ver Arquivo
@@ -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());
}
+1
Ver Arquivo
@@ -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,
+1 -1
Ver Arquivo
@@ -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)) {
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+19 -8
Ver Arquivo
@@ -789,8 +789,13 @@ void Parser::fixStaticVars() {
m_staticVars.pop_back();
}
void Parser::onFunction(Token &out, Token &ret, Token &ref, Token &name,
Token &params, Token &stmt, Token *attr) {
void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
Token &name, Token &params, 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 &params,
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 &params,
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,
+3 -3
Ver Arquivo
@@ -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 &params, Token &stmt, Token *attr);
void onFunction(Token &out, Token *modifier, Token &ret, Token &ref,
Token &name, Token &params, 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 &params,
Token &cparams, Token &stmts);
Token &cparams, Token &stmts, bool is_static);
void onClosureParam(Token &out, Token *params, Token &param, bool ref);
void onLabel(Token &out, Token &label);
void onGoto(Token &out, Token &label, bool limited);
+4 -4
Ver Arquivo
@@ -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) {
}
+3 -3
Ver Arquivo
@@ -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);
+14 -7
Ver Arquivo
@@ -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() {
+1 -1
Ver Arquivo
@@ -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));
}
+3 -2
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
+32
Ver Arquivo
@@ -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();
+10
Ver Arquivo
@@ -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
+1
Ver Arquivo
@@ -0,0 +1 @@
filepath.filter
+14 -5
Ver Arquivo
@@ -310,7 +310,7 @@ void create_generator(Parser *_p, Token &out, Token &params,
_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;}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+6 -6
Ver Arquivo
@@ -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 &params,
Token &cparams, Token &stmts) {
X(ret, ref, params, cparams, stmts);
void onClosure(Token &out, Token &ret, Token &ref, Token &params,
Token &cparams, Token &stmts, bool is_static) {
X(ret, ref, params, cparams, stmts, is_static);
}
void onClosureParam(Token &out, Token *params, Token &param, bool ref) {