Store variable arguments in optional local

Save 8 bytes of m_args and its initialization for Continuations without
func_get_args() call (does not save real memory due to 16-byte alignment).
Store variable arguments in optional local.
Esse commit está contido em:
Jan Oravec
2013-06-08 16:35:39 -07:00
commit de Sara Golemon
commit 9662396bf0
25 arquivos alterados com 1606 adições e 963 exclusões
+59 -45
Ver Arquivo
@@ -109,6 +109,11 @@ TRACE_SET_MOD(emitter)
using boost::dynamic_pointer_cast;
using boost::static_pointer_cast;
namespace {
const StringData* s_continuationVarArgsLocal
= StringData::GetStaticString("0ContinuationVarArgsLocal");
}
namespace StackSym {
static const char None = 0x00;
@@ -3217,6 +3222,38 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
return true;
}
}
} else if (call->isCallToFunction("func_num_args") &&
m_curFunc->isGenerator()) {
static const StringData* s_count =
StringData::GetStaticString("count");
emitVirtualLocal(m_curFunc->lookupVarId(s_continuationVarArgsLocal));
emitConvertToCell(e);
e.False();
e.FCallBuiltin(2, 1, s_count);
m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
e.UnboxR();
return true;
} else if (call->isCallToFunction("func_get_args") &&
m_curFunc->isGenerator()) {
emitVirtualLocal(m_curFunc->lookupVarId(s_continuationVarArgsLocal));
emitConvertToCell(e);
return true;
} else if (call->isCallToFunction("func_get_arg") &&
m_curFunc->isGenerator()) {
if (!params || params->getCount() == 0) {
e.Null();
return true;
}
visit((*params)[0]);
emitConvertToCell(e);
e.CastInt();
emitVirtualLocal(m_curFunc->lookupVarId(s_continuationVarArgsLocal));
emitConvertToCell(e);
e.False();
e.ArrayIdx();
return true;
} else if (call->isCompilerCallToFunction("hphp_unpack_continuation")) {
assert(!params || params->getCount() == 0);
int yieldLabelCount = call->getFunctionScope()->getYieldLabelCount();
@@ -3224,16 +3261,28 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
emitContinuationSwitch(e, yieldLabelCount);
return false;
} else if (call->isCompilerCallToFunction("hphp_create_continuation")) {
assert(params && (params->getCount() == 3 ||
params->getCount() == 4));
assert(params && params->getCount() == 3);
ExpressionPtr name = (*params)[1];
Variant nameVar;
UNUSED bool isScalar = name->getScalarValue(nameVar);
assert(isScalar && nameVar.isString());
if (m_curFunc->hasVar(s_continuationVarArgsLocal)) {
static const StringData* s_func_get_args =
StringData::GetStaticString("func_get_args");
Id local = m_curFunc->lookupVarId(s_continuationVarArgsLocal);
emitVirtualLocal(local);
e.FCallBuiltin(0, 0, s_func_get_args);
m_metaInfo.add(m_ue.bcPos(), Unit::MetaInfo::NopOut, false, 0, 0);
e.UnboxR();
emitSet(e);
e.PopC();
}
const StringData* nameStr =
StringData::GetStaticString(nameVar.getStringData());
bool callGetArgs = params->getCount() == 4;
e.CreateCont(callGetArgs, nameStr);
e.CreateCont(nameStr);
return true;
} else if (call->isCompilerCallToFunction("hphp_continuation_done")) {
assert(params && params->getCount() == 1);
@@ -5363,6 +5412,10 @@ void EmitterVisitor::emitPostponedMeths() {
}
}
if (p.m_meth->hasCallToGetArgs()) {
fe->allocVarId(s_continuationVarArgsLocal);
}
// Assign ids to all of the local variables eagerly. This gives us the
// nice property that all named local variables will be assigned ids
// 0 through k-1, while any unnamed local variable will have an id >= k.
@@ -5379,7 +5432,7 @@ void EmitterVisitor::emitPostponedMeths() {
if (allowOverride) attrs = attrs | AttrAllowOverride;
if (funcScope->mayUseVV()) {
if (funcScope->mayUseVV() || p.m_meth->hasCallToGetArgs()) {
attrs = attrs | AttrMayUseVV;
}
@@ -5901,12 +5954,6 @@ bool EmitterVisitor::canEmitBuiltinCall(FunctionCallPtr fn,
return false;
}
}
// Special handling for func_get_args and friends inside a generator.
if (m_curFunc->isGenerator() &&
(name == "func_get_args" || name == "func_num_args"
|| name == "func_get_arg")) {
return false;
}
// in sandbox mode, don't emit FCallBuiltin for redefinable functions
if (func->allowOverride() && !Option::WholeProgram) {
return false;
@@ -5977,46 +6024,13 @@ void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node) {
}
}
if (useFCallBuiltin) {
} else if (!m_curFunc->isGenerator()) {
if (!useFCallBuiltin) {
fpiStart = m_ue.bcPos();
if (nsName == nullptr) {
e.FPushFuncD(numParams, nLiteral);
} else {
e.FPushFuncU(numParams, nsName, nLiteral);
}
} else {
// Special handling for func_get_args and friends inside a generator.
const StringData* specialMethodName = nullptr;
static const StringData* contName =
StringData::GetStaticString(CONTINUATION_OBJECT_NAME);
Id contId = m_curFunc->lookupVarId(contName);
static const StringData* s_get_args =
StringData::GetStaticString("get_args");
static const StringData* s_num_args =
StringData::GetStaticString("num_args");
static const StringData* s_get_arg =
StringData::GetStaticString("get_arg");
if (nameStr == "func_get_args") {
specialMethodName = s_get_args;
} else if (nameStr == "func_num_args") {
specialMethodName = s_num_args;
} else if (nameStr == "func_get_arg") {
specialMethodName = s_get_arg;
}
if (nsName != nullptr) {
e.FPushFuncU(numParams, nsName, nLiteral);
} else if (specialMethodName != nullptr) {
emitVirtualLocal(contId);
emitCGet(e);
fpiStart = m_ue.bcPos();
e.FPushObjMethodD(numParams, specialMethodName);
} else {
fpiStart = m_ue.bcPos();
e.FPushFuncD(numParams, nLiteral);
}
}
} else {
// $foo()
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+7 -4
Ver Arquivo
@@ -115,7 +115,7 @@ extern void prepare_generator(Parser *_p, Token &stmt, Token &params);
extern void create_generator(Parser *_p, Token &out, Token &params,
Token &name, const std::string &closureName,
const char *clsname, Token *modifiers,
bool getArgs, Token &origGenFunc, bool isHhvm,
Token &origGenFunc, bool isHhvm,
Token *attr);
namespace HPHP {
@@ -858,7 +858,7 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
pushComment(comment);
Token origGenFunc;
create_generator(this, out, params, name, closureName, nullptr, nullptr,
hasCallToGetArgs, origGenFunc,
origGenFunc,
(!Option::WholeProgram || !Option::ParseTimeOpts),
attr);
m_closureGenerator = false;
@@ -867,6 +867,8 @@ void Parser::onFunction(Token &out, Token *modifiers, Token &ret, Token &ref,
assert(origStmt);
func->setOrigGeneratorFunc(origStmt);
origStmt->setGeneratorFunc(func);
origStmt->setHasCallToGetArgs(hasCallToGetArgs);
func->setHasCallToGetArgs(hasCallToGetArgs);
}
} else {
@@ -1155,7 +1157,7 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref,
pushComment(comment);
Token origGenFunc;
create_generator(this, out, params, name, closureName, m_clsName.c_str(),
&modifiers, hasCallToGetArgs, origGenFunc,
&modifiers, origGenFunc,
(!Option::WholeProgram || !Option::ParseTimeOpts),
attr);
MethodStatementPtr origStmt =
@@ -1163,7 +1165,8 @@ void Parser::onMethod(Token &out, Token &modifiers, Token &ret, Token &ref,
assert(origStmt);
mth->setOrigGeneratorFunc(origStmt);
origStmt->setGeneratorFunc(mth);
origStmt->setHasCallToGetArgs(hasCallToGetArgs);
mth->setHasCallToGetArgs(hasCallToGetArgs);
} else {
ExpressionListPtr attrList;
if (attr && attr->exp) {
+3 -3
Ver Arquivo
@@ -63,7 +63,7 @@ MethodStatement::MethodStatement
StatementListPtr stmt, int attr, const string &docComment,
ExpressionListPtr attrList, bool method /* = true */)
: Statement(STATEMENT_CONSTRUCTOR_BASE_PARAMETER_VALUES),
m_method(method), m_ref(ref), m_attribute(attr),
m_method(method), m_ref(ref), m_hasCallToGetArgs(false), m_attribute(attr),
m_cppLength(-1), m_modifiers(modifiers),
m_originalName(name), m_params(params),
m_retTypeConstraint(retTypeConstraint), m_stmt(stmt),
@@ -79,8 +79,8 @@ MethodStatement::MethodStatement
int attr, const string &docComment, ExpressionListPtr attrList,
bool method /* = true */)
: Statement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(MethodStatement)),
m_method(method), m_ref(ref), m_attribute(attr), m_cppLength(-1),
m_modifiers(modifiers), m_originalName(name),
m_method(method), m_ref(ref), m_hasCallToGetArgs(false), m_attribute(attr),
m_cppLength(-1), m_modifiers(modifiers), m_originalName(name),
m_params(params), m_retTypeConstraint(retTypeConstraint),
m_stmt(stmt), m_docComment(docComment), m_attrList(attrList) {
m_name = Util::toLower(name);
+4
Ver Arquivo
@@ -130,9 +130,13 @@ public:
void addTraitMethodToScope(AnalysisResultConstPtr ar,
ClassScopePtr classScope);
void setHasCallToGetArgs(bool f) { m_hasCallToGetArgs = f; }
bool hasCallToGetArgs() const { return m_hasCallToGetArgs; }
protected:
bool m_method;
bool m_ref;
bool m_hasCallToGetArgs;
int m_attribute;
int m_cppLength;
ModifierExpressionPtr m_modifiers;
+5 -6
Ver Arquivo
@@ -3680,13 +3680,12 @@ ArrayIdx [C C C] -> [C]
14. Continuation creation and execution
---------------------------------------
CreateCont <getargs> <function name> [] -> [C]
CreateCont <function name> [] -> [C]
Creates a GenericContinuation object and pushes it on the stack. The
Continuation will capture all defined local variables in the current
function, and if the <getargs> immediate is nonzero it will also store the
result of func_get_args(). The Continuation will store a reference to the
function named by the string immediate to be used as its body.
Creates a Continuation object and pushes it on the stack. The Continuation
will capture all defined local variables in the current function. The
Continuation will store a reference to the function named by the string
immediate to be used as its body.
ContEnter [] -> []
-34
Ver Arquivo
@@ -93,40 +93,6 @@
"args": [
]
},
{
"name": "num_args",
"return": {
"type": "Int64"
},
"flags": [
],
"args": [
]
},
{
"name": "get_args",
"return": {
"type": "VariantVec"
},
"flags": [
],
"args": [
]
},
{
"name": "get_arg",
"return": {
"type": "Variant"
},
"args": [
{
"name": "id",
"type": "Int64"
}
],
"flags": [
]
},
{
"name": "current",
"return": {
+1 -2
Ver Arquivo
@@ -436,10 +436,9 @@ public:
const Func* origFunc,
const Func* genFunc,
ObjectData* thisPtr,
ArrayData* args,
Class* frameStaticCls);
template<bool isMethod>
static c_Continuation* createContinuation(ActRec* fp, bool getArgs,
static c_Continuation* createContinuation(ActRec* fp,
const Func* origFunc,
const Func* genFunc);
static c_Continuation* fillContinuationVars(
-13
Ver Arquivo
@@ -91,19 +91,6 @@ int64_t c_Continuation::t_getlabel() {
return m_label;
}
int64_t c_Continuation::t_num_args() {
return m_args.size();
}
Array c_Continuation::t_get_args() {
return m_args;
}
Variant c_Continuation::t_get_arg(int64_t id) {
if (id < 0LL || id >= m_args.size()) return false;
return m_args.rvalAt(id, AccessFlags::Error);
}
Variant c_Continuation::t_current() {
const_assert(false);
return m_value;
+5 -13
Ver Arquivo
@@ -45,13 +45,6 @@ class c_Continuation : public ExtObjectData {
~c_Continuation();
public:
void init(const Func* origFunc,
ArrayData* args) noexcept {
m_origFunc = const_cast<Func*>(origFunc);
assert(m_origFunc);
m_args = args;
}
bool done() const { return o_subclassData.u8[0]; }
bool running() const { return o_subclassData.u8[1]; }
void setDone(bool done) { o_subclassData.u8[0] = done; }
@@ -67,9 +60,6 @@ public:
void t_update(int64_t label, CVarRef value);
Object t_getwaithandle();
int64_t t_getlabel();
int64_t t_num_args();
Array t_get_args();
Variant t_get_arg(int64_t id);
Variant t_current();
int64_t t_key();
void t_next();
@@ -81,14 +71,17 @@ public:
String t_getcalledclass();
Variant t___clone();
static c_Continuation* alloc(Class* cls, int nLocals, int nIters) {
static c_Continuation* alloc(const Func* origFunc, int nLocals, int nIters) {
assert(origFunc);
size_t arOffset = sizeof(c_Continuation) + sizeof(Iter) * nIters +
sizeof(TypedValue) * nLocals;
arOffset += sizeof(TypedValue) - 1;
arOffset &= ~(sizeof(TypedValue) - 1);
c_Continuation* cont =
(c_Continuation*)ALLOCOBJSZ(arOffset + sizeof(ActRec));
new ((void *)cont) c_Continuation(cls);
new ((void *)cont) c_Continuation();
cont->m_origFunc = const_cast<Func*>(origFunc);
cont->m_arPtr = (ActRec*)(uintptr_t(cont) + arOffset);
memset((void*)((uintptr_t)cont + sizeof(c_Continuation)), 0,
arOffset - sizeof(c_Continuation));
@@ -129,7 +122,6 @@ public:
Variant m_received;
Func *m_origFunc;
ActRec* m_arPtr;
Array m_args;
p_ContinuationWaitHandle m_waitHandle;
String& getCalledClass() { not_reached(); }
+7 -17
Ver Arquivo
@@ -6986,16 +6986,14 @@ c_Continuation*
VMExecutionContext::createContinuationHelper(const Func* origFunc,
const Func* genFunc,
ObjectData* thisPtr,
ArrayData* args,
Class* frameStaticCls) {
auto const cont = c_Continuation::alloc(
SystemLib::s_ContinuationClass,
origFunc,
genFunc->numLocals(),
genFunc->numIterators()
);
cont->incRefCount();
cont->setNoDestruct();
cont->init(origFunc, args);
// The ActRec corresponding to the generator body lives as long as the object
// does. We set it up once, here, and then just change FP to point to it when
@@ -7035,21 +7033,14 @@ VMExecutionContext::createContinuationHelper(const Func* origFunc,
template<bool isMethod>
c_Continuation*
VMExecutionContext::createContinuation(ActRec* fp,
bool getArgs,
const Func* origFunc,
const Func* genFunc) {
ObjectData* const thisPtr = fp->hasThis() ? fp->getThis() : nullptr;
Array args;
if (getArgs) {
args = hhvm_get_frame_args(fp);
}
return createContinuationHelper<isMethod>(
origFunc,
genFunc,
thisPtr,
args.get(),
frameStaticClass(fp)
);
}
@@ -7118,17 +7109,16 @@ VMExecutionContext::fillContinuationVars(ActRec* fp,
// Explicitly instantiate for hhbctranslator.o and codegen.o
template c_Continuation* VMExecutionContext::createContinuation<true>(
ActRec*, bool, const Func*, const Func*);
ActRec*, const Func*, const Func*);
template c_Continuation* VMExecutionContext::createContinuation<false>(
ActRec*, bool, const Func*, const Func*);
ActRec*, const Func*, const Func*);
template c_Continuation* VMExecutionContext::createContinuationHelper<true>(
const Func*, const Func*, ObjectData*, ArrayData*, Class*);
const Func*, const Func*, ObjectData*, Class*);
template c_Continuation* VMExecutionContext::createContinuationHelper<false>(
const Func*, const Func*, ObjectData*, ArrayData*, Class*);
const Func*, const Func*, ObjectData*, Class*);
inline void OPTBLD_INLINE VMExecutionContext::iopCreateCont(PC& pc) {
NEXT();
DECODE_IVA(getArgs);
DECODE_LITSTR(genName);
const Func* origFunc = m_fp->m_func;
@@ -7137,8 +7127,8 @@ inline void OPTBLD_INLINE VMExecutionContext::iopCreateCont(PC& pc) {
bool isMethod = origFunc->isMethod();
c_Continuation* cont = isMethod ?
createContinuation<true>(m_fp, getArgs, origFunc, genFunc) :
createContinuation<false>(m_fp, getArgs, origFunc, genFunc);
createContinuation<true>(m_fp, origFunc, genFunc) :
createContinuation<false>(m_fp, origFunc, genFunc);
fillContinuationVars(m_fp, origFunc, genFunc, cont);
+1 -1
Ver Arquivo
@@ -546,7 +546,7 @@ enum SetOpOp {
O(LateBoundCls, NA, NOV, ONE(AV), NF) \
O(NativeImpl, NA, NOV, NOV, CF_TF) \
O(CreateCl, TWO(IVA,SA), CVMANY, ONE(CV), NF) \
O(CreateCont, TWO(IVA,SA), NOV, ONE(CV), NF) \
O(CreateCont, ONE(SA), NOV, ONE(CV), NF) \
O(ContEnter, NA, NOV, NOV, CF) \
O(ContExit, NA, NOV, NOV, CF) \
O(UnpackCont, NA, NOV, TWO(CV,CV), NF) \
-1
Ver Arquivo
@@ -3499,7 +3499,6 @@ void CodeGenerator::cgInlineCreateCont(IRInstruction* inst) {
.immPtr(data.origFunc)
.immPtr(data.genFunc)
.ssa(inst->src(0))
.immPtr(nullptr) // getArgs array
// Deliberately ignoring frameStaticClass parameter, because
// it's unused if we have a $this pointer, and we don't inline
// functions with a null $this.
+2 -2
Ver Arquivo
@@ -467,8 +467,8 @@ void optimizeActRecs(IRTrace* trace, DceState& state, IRFactory* factory,
FTRACE(5, "CreateCont ({}) -> InlineCreateCont\n", inst->id());
CreateContData data;
data.origFunc = inst->src(3)->getValFunc();
data.genFunc = inst->src(4)->getValFunc();
data.origFunc = inst->src(2)->getValFunc();
data.genFunc = inst->src(3)->getValFunc();
assert(fp->inst()->src(0)->inst()->op() == SpillFrame);
auto const thisPtr = fp->inst()->src(0)->inst()->src(3);
+1 -3
Ver Arquivo
@@ -998,8 +998,7 @@ bool mapContParams(ContParamMap& map,
return true;
}
void HhbcTranslator::emitCreateCont(bool getArgs,
Id funNameStrId) {
void HhbcTranslator::emitCreateCont(Id funNameStrId) {
gen(ExitOnVarEnv, getExitSlowTrace()->front(), m_tb->fp());
auto const genName = lookupStringId(funNameStrId);
@@ -1015,7 +1014,6 @@ void HhbcTranslator::emitCreateCont(bool getArgs,
(TCA)&VMExecutionContext::createContinuation<false>
),
m_tb->fp(),
cns(getArgs),
cns(origFunc),
cns(genFunc)
);
+1 -1
Ver Arquivo
@@ -358,7 +358,7 @@ struct HhbcTranslator {
void emitVerifyParamType(uint32_t paramId);
// continuations
void emitCreateCont(bool getArgs, Id funNameStrId);
void emitCreateCont(Id funNameStrId);
void emitContEnter(int32_t returnBcOffset);
void emitContExitImpl();
void emitContExit();
-1
Ver Arquivo
@@ -436,7 +436,6 @@ O(Spill, DofS(0), SUnk, Mem) \
O(Reload, DofS(0), SUnk, Mem) \
O(CreateCont, D(Obj), C(TCA) \
S(FramePtr) \
C(Bool) \
C(Func) \
C(Func), E|N|Mem|PRc) \
O(InlineCreateCont, D(Obj), S(Obj,Null), E|N|PRc) \
+2 -2
Ver Arquivo
@@ -546,7 +546,7 @@ void Translator::translateDup(const NormalizedInstruction& ni) {
}
void Translator::translateCreateCont(const NormalizedInstruction& i) {
HHIR_EMIT(CreateCont, i.imm[0].u_IVA, i.imm[1].u_SA);
HHIR_EMIT(CreateCont, i.imm[0].u_SA);
}
void Translator::translateContEnter(const NormalizedInstruction& i) {
@@ -1198,7 +1198,7 @@ bool shouldIRInline(const Func* curFunc,
* Continuation allocation functions that take no arguments.
*/
resetCursor();
if (current == OpCreateCont && cursor->imm[0].u_IVA == 0) {
if (current == OpCreateCont) {
if (func->numParams()) {
FTRACE(1, "CreateCont with {} args\n", func->numParams());
}
+1 -1
Ver Arquivo
@@ -176,7 +176,7 @@ static CallMap s_callMap({
/* Continuation support helpers */
{CreateCont, {FSSA, 0}, DSSA, SNone,
{{SSA, 1}, {SSA, 2}, {SSA, 3}, {SSA, 4}}},
{{SSA, 1}, {SSA, 2}, {SSA, 3}}},
{InlineCreateCont, nullptr, DSSA, SSync,
{{Immed}, {Immed}, {SSA, 0}}},
{FillContLocals, (TCA)&VMExecutionContext::fillContinuationVars,
-16
Ver Arquivo
@@ -24092,22 +24092,6 @@ const char *g_class_map[] = {
(const char *)0xa /* KindOfInt64 */, NULL,
NULL,
NULL,
(const char *)0x10006040, "num_args", "", (const char*)0, (const char*)0,
" /**\n * ( excerpt from http://php.net/manual/en/continuation.num-args.php )\n *\n *\n * @return int\n */",
(const char *)0xa /* KindOfInt64 */, NULL,
NULL,
NULL,
(const char *)0x10006040, "get_args", "", (const char*)0, (const char*)0,
" /**\n * ( excerpt from http://php.net/manual/en/continuation.get-args.php )\n *\n *\n * @return vector\n */",
(const char *)0x20 /* KindOfArray */, NULL,
NULL,
NULL,
(const char *)0x10006040, "get_arg", "", (const char*)0, (const char*)0,
" /**\n * ( excerpt from http://php.net/manual/en/continuation.get-arg.php )\n *\n *\n * @id int\n *\n * @return mixed\n */",
(const char *)0xffffffff /* KindOfUnknown: $t: Variant */, (const char *)0x2000, "id", "", (const char *)0xa /* KindOfInt64 */, "", (const char *)0, "", (const char *)0, NULL,
NULL,
NULL,
NULL,
(const char *)0x10006040, "current", "", (const char*)0, (const char*)0,
" /**\n * ( excerpt from http://php.net/manual/en/continuation.current.php )\n *\n *\n * @return mixed\n */",
(const char *)0xffffffff /* KindOfUnknown: $t: Variant */, NULL,
+101
Ver Arquivo
@@ -0,0 +1,101 @@
<?php
function func_num_args_simple() {
return func_num_args();
}
function func_get_args_simple() {
return func_get_args();
}
function func_get_arg_simple($idx) {
return func_get_arg($idx);
}
function func_num_args_arg($arg) {
return func_num_args();
}
function func_get_args_arg($arg) {
return func_get_args();
}
function func_get_arg_arg($arg, $idx) {
return func_get_arg($idx);
}
function func_num_args_varenv($dst, $src) {
$$dst = $src;
return func_num_args();
}
function func_get_args_varenv($dst, $src) {
$$dst = $src;
return func_get_args();
}
function func_get_arg_varenv($dst, $src, $idx) {
$$dst = $src;
return func_get_arg($idx);
}
function test(string $generator, array $args) {
var_dump(call_user_func_array($generator, $args));
}
function test_num_args(string $type, array $extra_args) {
test(
'func_num_args_'.$type,
array_merge(
array_slice(func_get_args(), 2),
$extra_args
)
);
}
function test_get_args(string $type, array $extra_args) {
test(
'func_get_args_'.$type,
array_merge(
array_slice(func_get_args(), 2),
$extra_args
)
);
}
function test_get_arg(string $type, array $extra_args) {
$cnt = count($extra_args) + func_num_args() - 1;
for ($i = -1; $i <= $cnt; ++$i) {
test(
'func_get_arg_'.$type,
array_merge(
array_slice(func_get_args(), 2),
array($i),
$extra_args
)
);
}
}
$extra_args_set = array(
array(),
array('hello'),
array('hello', 47),
);
foreach ($extra_args_set as $extra_args) {
test_num_args('simple', $extra_args);
test_get_args('simple', $extra_args);
test_get_arg('simple', $extra_args);
test_num_args('arg', $extra_args, 'defined_arg');
test_get_args('arg', $extra_args, 'defined_arg');
test_get_arg('arg', $extra_args, 'defined_arg');
foreach (array('dst', 'src', 'idx', 'undef') as $dst) {
test_num_args('varenv', $extra_args, $dst, 'replacement');
test_get_args('varenv', $extra_args, $dst, 'replacement');
test_get_arg('varenv', $extra_args, $dst, 'replacement');
}
}
+273
Ver Arquivo
@@ -0,0 +1,273 @@
int(0)
array(0) {
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
int(0)
HipHop Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)
int(1)
array(1) {
[0]=>
string(11) "defined_arg"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(11) "defined_arg"
int(1)
HipHop Warning: func_get_arg(): Argument 2 not passed to function in %s on line %d
bool(false)
int(2)
array(2) {
[0]=>
string(11) "replacement"
[1]=>
string(11) "replacement"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(11) "replacement"
string(11) "replacement"
int(2)
HipHop Warning: func_get_arg(): Argument 3 not passed to function in %s on line %d
bool(false)
int(2)
array(2) {
[0]=>
string(3) "src"
[1]=>
string(11) "replacement"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(3) "src"
string(11) "replacement"
int(2)
HipHop Warning: func_get_arg(): Argument 3 not passed to function in %s on line %d
bool(false)
int(2)
array(2) {
[0]=>
string(3) "idx"
[1]=>
string(11) "replacement"
}
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
int(2)
array(2) {
[0]=>
string(5) "undef"
[1]=>
string(11) "replacement"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(5) "undef"
string(11) "replacement"
int(2)
HipHop Warning: func_get_arg(): Argument 3 not passed to function in %s on line %d
bool(false)
int(1)
array(1) {
[0]=>
string(5) "hello"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
int(0)
string(5) "hello"
HipHop Warning: func_get_arg(): Argument 2 not passed to function in %s on line %d
bool(false)
int(2)
array(2) {
[0]=>
string(11) "defined_arg"
[1]=>
string(5) "hello"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(11) "defined_arg"
int(1)
string(5) "hello"
HipHop Warning: func_get_arg(): Argument 3 not passed to function in %s on line %d
bool(false)
int(3)
array(3) {
[0]=>
string(11) "replacement"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(11) "replacement"
string(11) "replacement"
int(2)
string(5) "hello"
HipHop Warning: func_get_arg(): Argument 4 not passed to function in %s on line %d
bool(false)
int(3)
array(3) {
[0]=>
string(3) "src"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(3) "src"
string(11) "replacement"
int(2)
string(5) "hello"
HipHop Warning: func_get_arg(): Argument 4 not passed to function in %s on line %d
bool(false)
int(3)
array(3) {
[0]=>
string(3) "idx"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
int(3)
array(3) {
[0]=>
string(5) "undef"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(5) "undef"
string(11) "replacement"
int(2)
string(5) "hello"
HipHop Warning: func_get_arg(): Argument 4 not passed to function in %s on line %d
bool(false)
int(2)
array(2) {
[0]=>
string(5) "hello"
[1]=>
int(47)
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
int(0)
string(5) "hello"
int(47)
HipHop Warning: func_get_arg(): Argument 3 not passed to function in %s on line %d
bool(false)
int(3)
array(3) {
[0]=>
string(11) "defined_arg"
[1]=>
string(5) "hello"
[2]=>
int(47)
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(11) "defined_arg"
int(1)
string(5) "hello"
int(47)
HipHop Warning: func_get_arg(): Argument 4 not passed to function in %s on line %d
bool(false)
int(4)
array(4) {
[0]=>
string(11) "replacement"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(11) "replacement"
string(11) "replacement"
int(2)
string(5) "hello"
int(47)
HipHop Warning: func_get_arg(): Argument 5 not passed to function in %s on line %d
bool(false)
int(4)
array(4) {
[0]=>
string(3) "src"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(3) "src"
string(11) "replacement"
int(2)
string(5) "hello"
int(47)
HipHop Warning: func_get_arg(): Argument 5 not passed to function in %s on line %d
bool(false)
int(4)
array(4) {
[0]=>
string(3) "idx"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
int(4)
array(4) {
[0]=>
string(5) "undef"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
HipHop Warning: func_get_arg(): The argument number should be >= 0 in %s on line %d
bool(false)
string(5) "undef"
string(11) "replacement"
int(2)
string(5) "hello"
int(47)
HipHop Warning: func_get_arg(): Argument 5 not passed to function in %s on line %d
bool(false)
+104
Ver Arquivo
@@ -0,0 +1,104 @@
<?php
function gen_func_num_args_simple() {
yield func_num_args();
}
function gen_func_get_args_simple() {
yield func_get_args();
}
function gen_func_get_arg_simple($idx) {
yield func_get_arg($idx);
}
function gen_func_num_args_arg($arg) {
yield func_num_args();
}
function gen_func_get_args_arg($arg) {
yield func_get_args();
}
function gen_func_get_arg_arg($arg, $idx) {
yield func_get_arg($idx);
}
function gen_func_num_args_varenv($dst, $src) {
$$dst = $src;
yield func_num_args();
}
function gen_func_get_args_varenv($dst, $src) {
$$dst = $src;
yield func_get_args();
}
function gen_func_get_arg_varenv($dst, $src, $idx) {
$$dst = $src;
yield func_get_arg($idx);
}
function test(string $generator, array $args) {
$gen = call_user_func_array($generator, $args);
foreach ($gen as $val) {
var_dump($val);
}
}
function test_num_args(string $type, array $extra_args) {
test(
'gen_func_num_args_'.$type,
array_merge(
array_slice(func_get_args(), 2),
$extra_args
)
);
}
function test_get_args(string $type, array $extra_args) {
test(
'gen_func_get_args_'.$type,
array_merge(
array_slice(func_get_args(), 2),
$extra_args
)
);
}
function test_get_arg(string $type, array $extra_args) {
$cnt = count($extra_args) + func_num_args() - 1;
for ($i = -1; $i <= $cnt; ++$i) {
test(
'gen_func_get_arg_'.$type,
array_merge(
array_slice(func_get_args(), 2),
array($i),
$extra_args
)
);
}
}
$extra_args_set = array(
array(),
array('hello'),
array('hello', 47),
);
foreach ($extra_args_set as $extra_args) {
test_num_args('simple', $extra_args);
test_get_args('simple', $extra_args);
test_get_arg('simple', $extra_args);
test_num_args('arg', $extra_args, 'defined_arg');
test_get_args('arg', $extra_args, 'defined_arg');
test_get_arg('arg', $extra_args, 'defined_arg');
foreach (array('dst', 'src', 'idx', 'undef') as $dst) {
test_num_args('varenv', $extra_args, $dst, 'replacement');
test_get_args('varenv', $extra_args, $dst, 'replacement');
test_get_arg('varenv', $extra_args, $dst, 'replacement');
}
}
+243
Ver Arquivo
@@ -0,0 +1,243 @@
int(0)
array(0) {
}
bool(false)
int(0)
bool(false)
int(1)
array(1) {
[0]=>
string(11) "defined_arg"
}
bool(false)
string(11) "defined_arg"
int(1)
bool(false)
int(2)
array(2) {
[0]=>
string(3) "dst"
[1]=>
string(11) "replacement"
}
bool(false)
string(3) "dst"
string(11) "replacement"
int(2)
bool(false)
int(2)
array(2) {
[0]=>
string(3) "src"
[1]=>
string(11) "replacement"
}
bool(false)
string(3) "src"
string(11) "replacement"
int(2)
bool(false)
int(2)
array(2) {
[0]=>
string(3) "idx"
[1]=>
string(11) "replacement"
}
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
int(2)
array(2) {
[0]=>
string(5) "undef"
[1]=>
string(11) "replacement"
}
bool(false)
string(5) "undef"
string(11) "replacement"
int(2)
bool(false)
int(1)
array(1) {
[0]=>
string(5) "hello"
}
bool(false)
int(0)
string(5) "hello"
bool(false)
int(2)
array(2) {
[0]=>
string(11) "defined_arg"
[1]=>
string(5) "hello"
}
bool(false)
string(11) "defined_arg"
int(1)
string(5) "hello"
bool(false)
int(3)
array(3) {
[0]=>
string(3) "dst"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
bool(false)
string(3) "dst"
string(11) "replacement"
int(2)
string(5) "hello"
bool(false)
int(3)
array(3) {
[0]=>
string(3) "src"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
bool(false)
string(3) "src"
string(11) "replacement"
int(2)
string(5) "hello"
bool(false)
int(3)
array(3) {
[0]=>
string(3) "idx"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
int(3)
array(3) {
[0]=>
string(5) "undef"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
}
bool(false)
string(5) "undef"
string(11) "replacement"
int(2)
string(5) "hello"
bool(false)
int(2)
array(2) {
[0]=>
string(5) "hello"
[1]=>
int(47)
}
bool(false)
int(0)
string(5) "hello"
int(47)
bool(false)
int(3)
array(3) {
[0]=>
string(11) "defined_arg"
[1]=>
string(5) "hello"
[2]=>
int(47)
}
bool(false)
string(11) "defined_arg"
int(1)
string(5) "hello"
int(47)
bool(false)
int(4)
array(4) {
[0]=>
string(3) "dst"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
bool(false)
string(3) "dst"
string(11) "replacement"
int(2)
string(5) "hello"
int(47)
bool(false)
int(4)
array(4) {
[0]=>
string(3) "src"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
bool(false)
string(3) "src"
string(11) "replacement"
int(2)
string(5) "hello"
int(47)
bool(false)
int(4)
array(4) {
[0]=>
string(3) "idx"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
string(3) "idx"
int(4)
array(4) {
[0]=>
string(5) "undef"
[1]=>
string(11) "replacement"
[2]=>
string(5) "hello"
[3]=>
int(47)
}
bool(false)
string(5) "undef"
string(11) "replacement"
int(2)
string(5) "hello"
int(47)
bool(false)
+2 -8
Ver Arquivo
@@ -170,7 +170,7 @@ void prepare_generator(Parser *_p, Token &stmt, Token &params) {
// create a generator function with original name and parameters
void create_generator(Parser *_p, Token &out, Token &params,
Token &name, const std::string &closureName,
const char *clsname, Token *modifiers, bool getArgs,
const char *clsname, Token *modifiers,
Token &origGenFunc, bool isHhvm, Token *attr) {
_p->pushFuncLocation();
if (clsname) {
@@ -203,18 +203,12 @@ void create_generator(Parser *_p, Token &out, Token &params,
_p->onCallParam(param1, &param1, fname, false);
_p->onCallParam(param1, &param1, oname, false);
if (getArgs) {
Token cname; cname.setText("func_get_args");
Token empty;
Token call; _p->onCall(call, false, cname, empty, NULL);
_p->onCallParam(param1, &param1, call, false);
}
Token stmts0; _p->onStatementListStart(stmts0);
Token cname0; cname0.setText("hphp_create_continuation");
Token call; _p->onCall(call, false, cname0, param1, NULL, true);
Token ret; _p->onReturn(ret, &call);
Token stmts0; _p->onStatementListStart(stmts0);
Token stmts1; _p->addStatement(stmts1, stmts0, ret);
_p->finishStatement(scont, stmts1); scont = 1;
}