Opimize re-entry path
Use the jitted prologs, and add a "few args" re-entry point to avoid the overhead of creating an Array
Esse commit está contido em:
@@ -113,7 +113,7 @@ void ArrayIter::objInit(ObjectData *obj) {
|
||||
}
|
||||
default: {
|
||||
assert(obj->instanceof(SystemLib::s_IteratorClass));
|
||||
obj->o_invoke(s_rewind, Array());
|
||||
obj->o_invoke_few_args(s_rewind, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ bool ArrayIter::endHelper() {
|
||||
}
|
||||
default: {
|
||||
ObjectData* obj = getIteratorObj();
|
||||
return !obj->o_invoke(s_valid, Array());
|
||||
return !obj->o_invoke_few_args(s_valid, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@ void ArrayIter::nextHelper() {
|
||||
}
|
||||
default:
|
||||
ObjectData* obj = getIteratorObj();
|
||||
obj->o_invoke(s_next, Array());
|
||||
obj->o_invoke_few_args(s_next, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ Variant ArrayIter::firstHelper() {
|
||||
}
|
||||
default: {
|
||||
ObjectData* obj = getIteratorObj();
|
||||
return obj->o_invoke(s_key, Array());
|
||||
return obj->o_invoke_few_args(s_key, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +295,7 @@ Variant ArrayIter::second() {
|
||||
}
|
||||
default: {
|
||||
ObjectData* obj = getIteratorObj();
|
||||
return obj->o_invoke(s_current, Array());
|
||||
return obj->o_invoke_few_args(s_current, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,7 +340,7 @@ void ArrayIter::secondHelper(Variant& v) {
|
||||
}
|
||||
default: {
|
||||
ObjectData* obj = getIteratorObj();
|
||||
v = obj->o_invoke(s_current, Array());
|
||||
v = obj->o_invoke_few_args(s_current, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,9 +241,11 @@ struct ElmUCompare {
|
||||
const CallCtx* ctx;
|
||||
bool operator()(ElmT left, ElmT right) const {
|
||||
Variant ret;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), *ctx,
|
||||
CREATE_VECTOR2(acc.getValue(left),
|
||||
acc.getValue(right)));
|
||||
TypedValue args[2];
|
||||
tvDup(acc.getValue(left).asTypedValue(), args+0);
|
||||
tvDup(acc.getValue(right).asTypedValue(), args+1);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), *ctx,
|
||||
2, args);
|
||||
if (ret.isInteger()) {
|
||||
return ret.toInt64() < 0;
|
||||
}
|
||||
@@ -269,9 +271,10 @@ struct ElmUCompare {
|
||||
return false;
|
||||
}
|
||||
Variant ret2;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), *ctx,
|
||||
CREATE_VECTOR2(acc.getValue(right),
|
||||
acc.getValue(left)));
|
||||
tvDup(acc.getValue(right).asTypedValue(), args+0);
|
||||
tvDup(acc.getValue(left).asTypedValue(), args+1);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), *ctx,
|
||||
2, args);
|
||||
if (ret2.isBoolean()) {
|
||||
return ret2.toBoolean();
|
||||
}
|
||||
|
||||
@@ -884,7 +884,7 @@ bool empty(CVarRef v, CVarRef offset) {
|
||||
return collectionOffsetEmpty(obj, offset);
|
||||
} else {
|
||||
if (!Variant::GetArrayAccess(tva)->
|
||||
o_invoke(s_offsetExists, Array::Create(offset))) {
|
||||
o_invoke_few_args(s_offsetExists, 1, offset)) {
|
||||
return true;
|
||||
}
|
||||
return empty(v.rvalAt(offset));
|
||||
@@ -928,7 +928,7 @@ bool isset(CVarRef v, int64_t offset) {
|
||||
return collectionOffsetIsset(obj, offset);
|
||||
} else {
|
||||
return Variant::GetArrayAccess(tva)->
|
||||
o_invoke(s_offsetExists, Array::Create(offset), -1);
|
||||
o_invoke_few_args(s_offsetExists, 1, offset);
|
||||
}
|
||||
}
|
||||
if (Variant::IsString(tva)) {
|
||||
@@ -956,7 +956,7 @@ bool isset(CVarRef v, CVarRef offset) {
|
||||
return collectionOffsetIsset(obj, offset);
|
||||
} else {
|
||||
return Variant::GetArrayAccess(tva)->
|
||||
o_invoke(s_offsetExists, Array::Create(offset), -1);
|
||||
o_invoke_few_args(s_offsetExists, 1, offset);
|
||||
}
|
||||
}
|
||||
if (Variant::IsString(tva)) {
|
||||
|
||||
@@ -572,7 +572,6 @@ public:
|
||||
TypedValue* lookupClsCns(const StringData* cls,
|
||||
const StringData* cns);
|
||||
|
||||
ActRec* arGetSfp(const ActRec* ar);
|
||||
// Get the next outermost VM frame, even accross re-entry
|
||||
ActRec* getOuterVMFrame(const ActRec* ar);
|
||||
|
||||
@@ -676,24 +675,16 @@ public:
|
||||
CVarRef getEvaledArg(const StringData* val);
|
||||
private:
|
||||
void enterVMWork(ActRec* enterFnAr);
|
||||
void enterVM(TypedValue* retval,
|
||||
ActRec* ar,
|
||||
ExtraArgs* extraArgs);
|
||||
void reenterVM(TypedValue* retval,
|
||||
ActRec* ar,
|
||||
ExtraArgs* extraArgs,
|
||||
TypedValue* savedSP);
|
||||
void enterVMPrologue(ActRec* enterFnAr);
|
||||
void enterVM(TypedValue* retval, ActRec* ar);
|
||||
void reenterVM(TypedValue* retval, ActRec* ar, TypedValue* savedSP);
|
||||
void doFPushCuf(PC& pc, bool forward, bool safe);
|
||||
void unwindBuiltinFrame();
|
||||
template <bool forwarding>
|
||||
void pushClsMethodImpl(VM::Class* cls, StringData* name,
|
||||
ObjectData* obj, int numArgs);
|
||||
template <bool reenter>
|
||||
bool prepareFuncEntry(ActRec* ar,
|
||||
PC& pc,
|
||||
ExtraArgs* extraArgs);
|
||||
bool prepareArrayArgs(ActRec* ar, ArrayData* args,
|
||||
ExtraArgs*& extraArgs);
|
||||
bool prepareFuncEntry(ActRec* ar, PC& pc);
|
||||
bool prepareArrayArgs(ActRec* ar, ArrayData* args);
|
||||
void recordCodeCoverage(PC pc);
|
||||
bool isReturnHelper(uintptr_t address);
|
||||
int m_coverPrevLine;
|
||||
@@ -723,6 +714,25 @@ public:
|
||||
invokeFunc(retval, ctx.func, params, ctx.this_, ctx.cls, varEnv,
|
||||
ctx.invName, InvokeIgnoreByRefErrors);
|
||||
}
|
||||
void invokeFuncFew(TypedValue* retval,
|
||||
const HPHP::VM::Func* f,
|
||||
void* thisOrCls,
|
||||
StringData* invName,
|
||||
int argc, TypedValue* argv);
|
||||
void invokeFuncFew(TypedValue* retval,
|
||||
const HPHP::VM::Func* f,
|
||||
void* thisOrCls,
|
||||
StringData* invName = nullptr) {
|
||||
invokeFuncFew(retval, f, thisOrCls, invName, 0, nullptr);
|
||||
}
|
||||
void invokeFuncFew(TypedValue* retval,
|
||||
const CallCtx& ctx,
|
||||
int argc, TypedValue* argv) {
|
||||
invokeFuncFew(retval, ctx.func,
|
||||
ctx.this_ ? (void*)ctx.this_ :
|
||||
ctx.cls ? (char*)ctx.cls + 1 : nullptr,
|
||||
ctx.invName, argc, argv);
|
||||
}
|
||||
void invokeContFunc(const HPHP::VM::Func* f,
|
||||
ObjectData* this_,
|
||||
TypedValue* param = nullptr);
|
||||
|
||||
@@ -52,8 +52,7 @@ UserFile::UserFile(VM::Class *cls, int options /*= 0 */,
|
||||
m_obj = VM::Instance::newInstance(cls);
|
||||
m_obj.o_set("context", context);
|
||||
Variant ret;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctor,
|
||||
Array::Create(), m_obj.get());
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctor, m_obj.get());
|
||||
|
||||
m_StreamOpen = lookupMethod(s_stream_open.get());
|
||||
m_StreamClose = lookupMethod(s_stream_close.get());
|
||||
|
||||
@@ -87,7 +87,7 @@ void ObjectData::destruct() {
|
||||
tvWriteNull(&retval);
|
||||
try {
|
||||
// Call the destructor method
|
||||
g_vmContext->invokeFunc(&retval, meth, null_array, this);
|
||||
g_vmContext->invokeFuncFew(&retval, meth, this);
|
||||
} catch (...) {
|
||||
// Swallow any exceptions that escape the __destruct method
|
||||
handle_destructor_exception();
|
||||
@@ -142,7 +142,7 @@ Object ObjectData::iterableObject(bool& isIterable,
|
||||
}
|
||||
Object obj(this);
|
||||
while (obj->instanceof(SystemLib::s_IteratorAggregateClass)) {
|
||||
Variant iterator = obj->o_invoke(s_getIterator, Array());
|
||||
Variant iterator = obj->o_invoke_few_args(s_getIterator, 0);
|
||||
if (!iterator.isObject()) break;
|
||||
ObjectData* o = iterator.getObjectData();
|
||||
if (o->instanceof(SystemLib::s_IteratorClass)) {
|
||||
@@ -469,86 +469,79 @@ Array ObjectData::o_toIterArray(CStrRef context,
|
||||
return Array(retval);
|
||||
}
|
||||
|
||||
Variant ObjectData::o_invoke(CStrRef s, CArrRef params,
|
||||
strhash_t hash /* = -1 */,
|
||||
bool fatal /* = true */) {
|
||||
static bool decode_invoke(CStrRef s, ObjectData* obj, bool fatal,
|
||||
CallCtx& ctx) {
|
||||
// TODO This duplicates some logic from vm_decode_function and
|
||||
// vm_call_user_func, we should refactor this in the near future
|
||||
ObjectData* this_ = this;
|
||||
HPHP::VM::Class* cls = getVMClass();
|
||||
StringData* invName = nullptr;
|
||||
ctx.this_ = obj;
|
||||
ctx.cls = obj->getVMClass();
|
||||
ctx.invName = nullptr;
|
||||
|
||||
// XXX The lookup below doesn't take context into account, so it will lead
|
||||
// to incorrect behavior in some corner cases. o_invoke is gradually being
|
||||
// removed from the HPHP runtime this should be ok for the short term.
|
||||
const HPHP::VM::Func* f = cls->lookupMethod(s.get());
|
||||
if (f && (f->attrs() & AttrStatic)) {
|
||||
// If we found a method and its static, null out this_
|
||||
this_ = nullptr;
|
||||
} else if (!f) {
|
||||
if (this_) {
|
||||
// If this_ is non-null AND we could not find a method, try
|
||||
// looking up __call in cls's method table
|
||||
f = cls->lookupMethod(s___call.get());
|
||||
ctx.func = ctx.cls->lookupMethod(s.get());
|
||||
if (ctx.func) {
|
||||
if (ctx.func->attrs() & AttrStatic) {
|
||||
// If we found a method and its static, null out this_
|
||||
ctx.this_ = nullptr;
|
||||
}
|
||||
if (!f) {
|
||||
} else {
|
||||
// If this_ is non-null AND we could not find a method, try
|
||||
// looking up __call in cls's method table
|
||||
ctx.func = ctx.cls->lookupMethod(s___call.get());
|
||||
|
||||
if (!ctx.func) {
|
||||
// Bail if we couldn't find the method or __call
|
||||
o_invoke_failed(o_getClassName().data(), s.data(), fatal);
|
||||
return uninit_null();
|
||||
o_invoke_failed(ctx.cls->name()->data(), s.data(), fatal);
|
||||
return false;
|
||||
}
|
||||
// We found __call! Stash the original name into invName.
|
||||
assert(!(f->attrs() & AttrStatic));
|
||||
invName = s.get();
|
||||
invName->incRefCount();
|
||||
assert(!(ctx.func->attrs() & AttrStatic));
|
||||
ctx.invName = s.get();
|
||||
ctx.invName->incRefCount();
|
||||
}
|
||||
assert(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
Variant ObjectData::o_invoke(CStrRef s, CArrRef params,
|
||||
bool fatal /* = true */) {
|
||||
CallCtx ctx;
|
||||
if (!decode_invoke(s, this, fatal, ctx)) return Variant(Variant::nullInit);
|
||||
Variant ret;
|
||||
g_vmContext->invokeFunc((TypedValue*)&ret, f, params, this_, cls,
|
||||
nullptr, invName);
|
||||
g_vmContext->invokeFunc((TypedValue*)&ret, ctx, params);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define APPEND_1_ARGS(params) params.append(a0);
|
||||
#define APPEND_2_ARGS(params) APPEND_1_ARGS(params); params.append(a1)
|
||||
#define APPEND_3_ARGS(params) APPEND_2_ARGS(params); params.append(a2)
|
||||
#define APPEND_4_ARGS(params) APPEND_3_ARGS(params); params.append(a3)
|
||||
#define APPEND_5_ARGS(params) APPEND_4_ARGS(params); params.append(a4)
|
||||
#define APPEND_6_ARGS(params) APPEND_5_ARGS(params); params.append(a5)
|
||||
#define APPEND_7_ARGS(params) APPEND_6_ARGS(params); params.append(a6)
|
||||
#define APPEND_8_ARGS(params) APPEND_7_ARGS(params); params.append(a7)
|
||||
#define APPEND_9_ARGS(params) APPEND_8_ARGS(params); params.append(a8)
|
||||
#define APPEND_10_ARGS(params) APPEND_9_ARGS(params); params.append(a9)
|
||||
|
||||
Variant ObjectData::o_invoke_few_args(CStrRef s, strhash_t hash, int count,
|
||||
Variant ObjectData::o_invoke_few_args(CStrRef s, int count,
|
||||
INVOKE_FEW_ARGS_IMPL_ARGS) {
|
||||
Array params = Array::Create();
|
||||
|
||||
CallCtx ctx;
|
||||
if (!decode_invoke(s, this, true, ctx)) return Variant(Variant::nullInit);
|
||||
|
||||
TypedValue args[INVOKE_FEW_ARGS_COUNT];
|
||||
switch(count) {
|
||||
case 1: APPEND_1_ARGS(params);
|
||||
break;
|
||||
case 2: APPEND_2_ARGS(params);
|
||||
break;
|
||||
case 3: APPEND_3_ARGS(params);
|
||||
break;
|
||||
#if INVOKE_FEW_ARGS_COUNT > 3
|
||||
case 4: APPEND_4_ARGS(params);
|
||||
break;
|
||||
case 5: APPEND_5_ARGS(params);
|
||||
break;
|
||||
case 6: APPEND_6_ARGS(params);
|
||||
break;
|
||||
#if INVOKE_FEW_ARGS_COUNT > 6
|
||||
case 7: APPEND_7_ARGS(params);
|
||||
break;
|
||||
case 8: APPEND_8_ARGS(params);
|
||||
break;
|
||||
case 9: APPEND_9_ARGS(params);
|
||||
break;
|
||||
case 10: APPEND_10_ARGS(params);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
default: not_implemented();
|
||||
#if INVOKE_FEW_ARGS_COUNT > 6
|
||||
case 10: tvDup(a9.asTypedValue(), args + 9);
|
||||
case 9: tvDup(a8.asTypedValue(), args + 8);
|
||||
case 8: tvDup(a7.asTypedValue(), args + 7);
|
||||
case 7: tvDup(a6.asTypedValue(), args + 6);
|
||||
#endif
|
||||
#if INVOKE_FEW_ARGS_COUNT > 3
|
||||
case 6: tvDup(a5.asTypedValue(), args + 5);
|
||||
case 5: tvDup(a4.asTypedValue(), args + 4);
|
||||
case 4: tvDup(a3.asTypedValue(), args + 3);
|
||||
#endif
|
||||
case 3: tvDup(a2.asTypedValue(), args + 2);
|
||||
case 2: tvDup(a1.asTypedValue(), args + 1);
|
||||
case 1: tvDup(a0.asTypedValue(), args + 0);
|
||||
case 0: break;
|
||||
}
|
||||
return o_invoke(s, params, hash);
|
||||
|
||||
Variant ret;
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&ret, ctx, count, args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ObjectData::php_sleep(Variant &ret) {
|
||||
@@ -576,7 +569,7 @@ void ObjectData::serializeImpl(VariableSerializer *serializer) const {
|
||||
if (instanceof(SystemLib::s_SerializableClass)) {
|
||||
assert(!isCollection());
|
||||
Variant ret =
|
||||
const_cast<ObjectData*>(this)->o_invoke(s_serialize, Array(), -1);
|
||||
const_cast<ObjectData*>(this)->o_invoke_few_args(s_serialize, 0);
|
||||
if (ret.isString()) {
|
||||
serializer->writeSerializableObject(o_getClassName(), ret.toString());
|
||||
} else if (ret.isNull()) {
|
||||
@@ -594,7 +587,7 @@ void ObjectData::serializeImpl(VariableSerializer *serializer) const {
|
||||
assert(!isCollection());
|
||||
try {
|
||||
Variant ret =
|
||||
const_cast<ObjectData*>(this)->o_invoke(s_serialize, Array(), -1);
|
||||
const_cast<ObjectData*>(this)->o_invoke_few_args(s_serialize, 0);
|
||||
if (ret.isString()) {
|
||||
serializer->writeSerializableObject(o_getClassName(), ret.toString());
|
||||
} else if (ret.isNull()) {
|
||||
@@ -735,8 +728,10 @@ Variant ObjectData::offsetGet(Variant key) {
|
||||
return uninit_null();
|
||||
}
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)(&v), method,
|
||||
CREATE_VECTOR1(key), this);
|
||||
TypedValue args[1];
|
||||
tvDup(key.asTypedValue(), args + 0);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)(&v), method,
|
||||
this, nullptr, 1, args);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
@@ -180,9 +180,8 @@ class ObjectData : public CountableNF {
|
||||
static Object FromArray(ArrayData *properties);
|
||||
|
||||
// method invocation with CStrRef
|
||||
Variant o_invoke(CStrRef s, CArrRef params, strhash_t hash = -1,
|
||||
bool fatal = true);
|
||||
Variant o_invoke_few_args(CStrRef s, strhash_t hash, int count,
|
||||
Variant o_invoke(CStrRef s, CArrRef params, bool fatal = true);
|
||||
Variant o_invoke_few_args(CStrRef s, int count,
|
||||
INVOKE_FEW_ARGS_DECL_ARGS);
|
||||
|
||||
// misc
|
||||
|
||||
@@ -1840,7 +1840,7 @@ void Variant::callOffsetUnset(CVarRef key) {
|
||||
if (LIKELY(obj->isCollection())) {
|
||||
collectionOffsetUnset(obj, key);
|
||||
} else {
|
||||
getArrayAccess()->o_invoke(s_offsetUnset, Array::Create(key));
|
||||
getArrayAccess()->o_invoke_few_args(s_offsetUnset, 1, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1863,8 +1863,8 @@ static void raise_bad_offset_notice() {
|
||||
if (obj->isCollection()) { \
|
||||
return collectionOffsetGet(obj, offset); \
|
||||
} else { \
|
||||
return getArrayAccess()->o_invoke(s_offsetGet, \
|
||||
Array::Create(offset)); \
|
||||
return getArrayAccess()->o_invoke_few_args(s_offsetGet, \
|
||||
1, offset); \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
@@ -1896,7 +1896,7 @@ Variant Variant::rvalAtHelper(int64_t offset, ACCESSPARAMS_IMPL) const {
|
||||
if (LIKELY(obj->isCollection())) {
|
||||
return collectionOffsetGet(obj, offset);
|
||||
} else {
|
||||
return getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
|
||||
return getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1942,7 +1942,7 @@ Variant Variant::rvalAt(CStrRef offset, ACCESSPARAMS_IMPL) const {
|
||||
if (LIKELY(obj->isCollection())) {
|
||||
return collectionOffsetGet(obj, offset);
|
||||
} else {
|
||||
return getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
|
||||
return getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2008,7 +2008,7 @@ Variant Variant::rvalAt(CVarRef offset, ACCESSPARAMS_IMPL) const {
|
||||
if (LIKELY(obj->isCollection())) {
|
||||
return collectionOffsetGet(obj, offset);
|
||||
} else {
|
||||
return getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
|
||||
return getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2039,7 +2039,7 @@ CVarRef Variant::rvalRefHelper(T offset, CVarRef tmp, ACCESSPARAMS_IMPL) const {
|
||||
return collectionOffsetGet(obj, offset);
|
||||
} else {
|
||||
const_cast<Variant&>(tmp) =
|
||||
getArrayAccess()->o_invoke(s_offsetGet, Array::Create(offset));
|
||||
getArrayAccess()->o_invoke_few_args(s_offsetGet, 1, offset);
|
||||
return tmp;
|
||||
}
|
||||
break;
|
||||
@@ -2315,9 +2315,8 @@ Variant &Variant::lvalAt() {
|
||||
if (obj->isCollection()) {
|
||||
raise_error("Cannot use [] for reading");
|
||||
}
|
||||
Array params = CREATE_VECTOR1(uninit_null());
|
||||
Variant& ret = lvalBlackHole();
|
||||
ret = m_data.pobj->o_invoke(s_offsetGet, params);
|
||||
ret = m_data.pobj->o_invoke_few_args(s_offsetGet, 1, init_null_variant);
|
||||
raise_warning("Indirect modification of overloaded element of %s has "
|
||||
"no effect", m_data.pobj->o_getClassName().data());
|
||||
return ret;
|
||||
@@ -2398,23 +2397,23 @@ Variant Variant::o_setRef(CStrRef propName, CVarRef val,
|
||||
return m_data.pobj->o_setRef(propName, val, context);
|
||||
}
|
||||
|
||||
Variant Variant::o_invoke(CStrRef s, CArrRef params, int64_t hash /* = -1 */) {
|
||||
Variant Variant::o_invoke(CStrRef s, CArrRef params) {
|
||||
if (m_type == KindOfObject) {
|
||||
return m_data.pobj->o_invoke(s, params, hash);
|
||||
return m_data.pobj->o_invoke(s, params);
|
||||
} else if (m_type == KindOfRef) {
|
||||
return m_data.pref->var()->o_invoke(s, params, hash);
|
||||
return m_data.pref->var()->o_invoke(s, params);
|
||||
} else {
|
||||
throw_call_non_object(s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Variant Variant::o_invoke_few_args(CStrRef s, int64_t hash, int count,
|
||||
Variant Variant::o_invoke_few_args(CStrRef s, int count,
|
||||
INVOKE_FEW_ARGS_IMPL_ARGS) {
|
||||
if (m_type == KindOfObject) {
|
||||
return m_data.pobj->o_invoke_few_args(s, hash, count,
|
||||
return m_data.pobj->o_invoke_few_args(s, count,
|
||||
INVOKE_FEW_ARGS_PASS_ARGS);
|
||||
} else if (m_type == KindOfRef) {
|
||||
return m_data.pref->var()->o_invoke_few_args(s, hash, count,
|
||||
return m_data.pref->var()->o_invoke_few_args(s, count,
|
||||
INVOKE_FEW_ARGS_PASS_ARGS);
|
||||
} else {
|
||||
throw_call_non_object(s.c_str());
|
||||
@@ -2476,7 +2475,7 @@ inline ALWAYS_INLINE CVarRef Variant::SetImpl(Variant *self, T key,
|
||||
if (obj->isCollection()) {
|
||||
collectionOffsetSet(obj, key, v);
|
||||
} else {
|
||||
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, -1, 2, key, v);
|
||||
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, 2, key, v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2533,8 +2532,7 @@ CVarRef Variant::append(CVarRef v) {
|
||||
if (LIKELY(obj->isCollection())) {
|
||||
collectionOffsetAppend(obj, v);
|
||||
} else {
|
||||
Array params = CREATE_VECTOR2(uninit_null(), v);
|
||||
obj->o_invoke(s_offsetSet, params);
|
||||
obj->o_invoke_few_args(s_offsetSet, 2, init_null_variant, v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2602,7 +2600,7 @@ inline ALWAYS_INLINE CVarRef Variant::SetRefImpl(Variant *self, T key,
|
||||
if (self->m_data.pobj->isCollection()) {
|
||||
raise_error("An element of a collection cannot be taken by reference");
|
||||
}
|
||||
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, -1, 2, key, v);
|
||||
self->getArrayAccess()->o_invoke_few_args(s_offsetSet, 2, key, v);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2658,7 +2656,7 @@ CVarRef Variant::appendRef(CVarRef v) {
|
||||
if (LIKELY(obj->isCollection())) {
|
||||
raise_error("Collection elements cannot be taken by reference");
|
||||
} else {
|
||||
obj->o_invoke_few_args(s_offsetSet, -1, 2, uninit_null(), v);
|
||||
obj->o_invoke_few_args(s_offsetSet, 2, uninit_null(), v);
|
||||
}
|
||||
}
|
||||
case KindOfStaticString:
|
||||
@@ -3147,7 +3145,7 @@ void Variant::unserialize(VariableUnserializer *uns,
|
||||
if (!obj->instanceof(SystemLib::s_SerializableClass)) {
|
||||
raise_error("%s didn't implement Serializable", clsName.data());
|
||||
}
|
||||
obj->o_invoke(s_unserialize, CREATE_VECTOR1(serialized), -1);
|
||||
obj->o_invoke_few_args(s_unserialize, 1, serialized);
|
||||
obj.get()->clearNoDestruct();
|
||||
} catch (ClassNotFoundException &e) {
|
||||
if (!uns->allowUnknownSerializableClass()) {
|
||||
|
||||
@@ -892,8 +892,8 @@ class Variant : private TypedValue {
|
||||
}
|
||||
Variant o_setRef(CStrRef s, CVarRef v, CStrRef context = null_string);
|
||||
|
||||
Variant o_invoke(CStrRef s, CArrRef params, int64_t hash = -1);
|
||||
Variant o_invoke_few_args(CStrRef s, int64_t hash, int count,
|
||||
Variant o_invoke(CStrRef s, CArrRef params);
|
||||
Variant o_invoke_few_args(CStrRef s, int count,
|
||||
INVOKE_FEW_ARGS_DECL_ARGS);
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -28,12 +28,11 @@ static StaticString s_write("write");
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
String PhpThriftBuffer::readImpl() {
|
||||
Array args(CREATE_VECTOR1(m_size));
|
||||
return m_xin->o_invoke(s_read, args, -1);
|
||||
return m_xin->o_invoke_few_args(s_read, 1, m_size);
|
||||
}
|
||||
|
||||
void PhpThriftBuffer::flushImpl(CStrRef data) {
|
||||
m_xout->o_invoke(s_write, CREATE_VECTOR1(data), -1);
|
||||
m_xout->o_invoke_few_args(s_write, 1, data);
|
||||
}
|
||||
|
||||
void PhpThriftBuffer::throwError(const char *msg, int code) {
|
||||
|
||||
@@ -378,7 +378,7 @@ void VariableSerializer::write(CObjRef v) {
|
||||
|
||||
if (v.instanceof(SystemLib::s_JsonSerializableClass)) {
|
||||
assert(!v->isCollection());
|
||||
Variant ret = v->o_invoke(s_jsonSerialize, null_array, -1);
|
||||
Variant ret = v->o_invoke_few_args(s_jsonSerialize, 0);
|
||||
// for non objects or when $this is returned
|
||||
if (!ret.isObject() || (ret.isObject() && !ret.same(v))) {
|
||||
write(ret);
|
||||
|
||||
@@ -81,7 +81,7 @@ void CmdUser::invokeList(DebuggerClient *client, const std::string &cls) {
|
||||
pclient->m_client = client;
|
||||
try {
|
||||
Object cmd = create_object(cls.c_str(), null_array);
|
||||
cmd->o_invoke(s_onAutoComplete, CREATE_VECTOR1(pclient), -1);
|
||||
cmd->o_invoke_few_args(s_onAutoComplete, 1, pclient);
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ bool CmdUser::invokeHelp(DebuggerClient *client, const std::string &cls) {
|
||||
pclient->m_client = client;
|
||||
try {
|
||||
Object cmd = create_object(cls.c_str(), null_array);
|
||||
Variant ret = cmd->o_invoke(s_help, CREATE_VECTOR1(pclient), -1);
|
||||
Variant ret = cmd->o_invoke_few_args(s_help, 1, pclient);
|
||||
return !same(ret, false);
|
||||
} catch (...) {}
|
||||
return false;
|
||||
@@ -101,7 +101,7 @@ bool CmdUser::invokeClient(DebuggerClient *client, const std::string &cls) {
|
||||
pclient->m_client = client;
|
||||
try {
|
||||
Object cmd = create_object(cls.c_str(), null_array);
|
||||
Variant ret = cmd->o_invoke(s_onClient, CREATE_VECTOR1(pclient), -1);
|
||||
Variant ret = cmd->o_invoke_few_args(s_onClient, 1, pclient);
|
||||
return !same(ret, false);
|
||||
} catch (...) {}
|
||||
return false;
|
||||
@@ -112,7 +112,7 @@ bool CmdUser::onServer(DebuggerProxy *proxy) {
|
||||
p_DebuggerProxyCmdUser pproxy(NEWOBJ(c_DebuggerProxyCmdUser)());
|
||||
pproxy->m_proxy = proxy;
|
||||
try {
|
||||
Variant ret = m_cmd->o_invoke(s_onServer, CREATE_VECTOR1(pproxy), -1);
|
||||
Variant ret = m_cmd->o_invoke_few_args(s_onServer, 1, pproxy);
|
||||
return !same(ret, false);
|
||||
} catch (...) {}
|
||||
return false;
|
||||
|
||||
@@ -740,10 +740,10 @@ int DebuggerProxy::getStackDepth() {
|
||||
VMExecutionContext* context = g_vmContext;
|
||||
ActRec *fp = context->getFP();
|
||||
if (!fp) return 0;
|
||||
ActRec *prev = context->arGetSfp(fp);
|
||||
ActRec *prev = fp->arGetSfp();
|
||||
while (fp != prev) {
|
||||
fp = prev;
|
||||
prev = context->arGetSfp(fp);
|
||||
prev = fp->arGetSfp();
|
||||
depth++;
|
||||
}
|
||||
return depth;
|
||||
|
||||
@@ -163,7 +163,9 @@ Variant f_array_fill(int start_index, int num, CVarRef value) {
|
||||
static bool filter_func(CVarRef value, const void *data) {
|
||||
CallCtx* ctx = (CallCtx*)data;
|
||||
Variant ret;
|
||||
g_vmContext->invokeFunc((TypedValue*)&ret, *ctx, CREATE_VECTOR1(value));
|
||||
TypedValue args[1];
|
||||
tvDup(value.asTypedValue(), args + 0);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&ret, *ctx, 1, args);
|
||||
return ret.toBoolean();
|
||||
}
|
||||
Variant f_array_filter(CVarRef input, CVarRef callback /* = null_variant */) {
|
||||
@@ -459,8 +461,10 @@ Variant f_array_rand(CVarRef input, int num_req /* = 1 */) {
|
||||
static Variant reduce_func(CVarRef result, CVarRef operand, const void *data) {
|
||||
CallCtx* ctx = (CallCtx*)data;
|
||||
Variant ret;
|
||||
g_vmContext->invokeFunc((TypedValue*)&ret, *ctx,
|
||||
CREATE_VECTOR2(result, operand));
|
||||
TypedValue args[2];
|
||||
tvDup(result.asTypedValue(), args + 0);
|
||||
tvDup(operand.asTypedValue(), args + 1);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&ret, *ctx, 2, args);
|
||||
return ret;
|
||||
}
|
||||
Variant f_array_reduce(CVarRef input, CVarRef callback,
|
||||
@@ -565,8 +569,11 @@ static void walk_func(VRefParam value, CVarRef key, CVarRef userdata,
|
||||
const void *data) {
|
||||
CallCtx* ctx = (CallCtx*)data;
|
||||
Variant sink;
|
||||
g_vmContext->invokeFunc((TypedValue*)&sink, *ctx,
|
||||
CREATE_VECTOR3(ref(value), key, userdata));
|
||||
TypedValue args[3];
|
||||
tvDup(value->asTypedValue(), args + 0);
|
||||
tvDup(key.asTypedValue(), args + 1);
|
||||
tvDup(userdata.asTypedValue(), args + 2);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&sink, *ctx, 3, args);
|
||||
}
|
||||
bool f_array_walk_recursive(VRefParam input, CVarRef funcname,
|
||||
CVarRef userdata /* = null_variant */) {
|
||||
@@ -653,7 +660,7 @@ int64_t f_count(CVarRef var, bool recursive /* = false */) {
|
||||
{
|
||||
Object obj = var.toObject();
|
||||
if (obj.instanceof(SystemLib::s_CountableClass)) {
|
||||
return obj->o_invoke(s_count, null_array, -1);
|
||||
return obj->o_invoke_few_args(s_count, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -300,12 +300,12 @@ Variant f_get_object_vars(CVarRef object) {
|
||||
|
||||
Variant f_call_user_method_array(CStrRef method_name, VRefParam obj,
|
||||
CArrRef paramarr) {
|
||||
return obj.toObject()->o_invoke(method_name, paramarr, -1);
|
||||
return obj.toObject()->o_invoke(method_name, paramarr);
|
||||
}
|
||||
|
||||
Variant f_call_user_method(int _argc, CStrRef method_name, VRefParam obj,
|
||||
CArrRef _argv /* = null_array */) {
|
||||
return obj.toObject()->o_invoke(method_name, _argv, -1);
|
||||
return obj.toObject()->o_invoke(method_name, _argv);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -522,7 +522,9 @@ Object c_Vector::t_map(CVarRef callback) {
|
||||
for (uint i = 0; i < m_size; ++i) {
|
||||
TypedValue* tv = &vec->m_data[i];
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(tv, ctx, CREATE_VECTOR1(tvAsCVarRef(&m_data[i])));
|
||||
TypedValue args[1];
|
||||
tvDup(&m_data[i], args + 0);
|
||||
g_vmContext->invokeFuncFew(tv, ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
tvRefcountedDecRef(tv);
|
||||
throw_collection_modified();
|
||||
@@ -545,8 +547,9 @@ Object c_Vector::t_filter(CVarRef callback) {
|
||||
for (uint i = 0; i < m_size; ++i) {
|
||||
Variant ret;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&m_data[i])));
|
||||
TypedValue args[1];
|
||||
tvDup(&m_data[i], args + 0);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
throw_collection_modified();
|
||||
}
|
||||
@@ -1463,7 +1466,9 @@ Object c_Map::t_map(CVarRef callback) {
|
||||
}
|
||||
TypedValue* tv = &np.data;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(tv, ctx, CREATE_VECTOR1(tvAsCVarRef(&p.data)));
|
||||
TypedValue args[1];
|
||||
tvDup(&p.data, args + 0);
|
||||
g_vmContext->invokeFuncFew(tv, ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
tvRefcountedDecRef(tv);
|
||||
throw_collection_modified();
|
||||
@@ -1493,8 +1498,9 @@ Object c_Map::t_filter(CVarRef callback) {
|
||||
if (!p.validValue()) continue;
|
||||
Variant ret;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&p.data)));
|
||||
TypedValue args[1];
|
||||
tvDup(&p.data, args + 0);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
throw_collection_modified();
|
||||
}
|
||||
@@ -2629,8 +2635,9 @@ Object c_StableMap::t_map(CVarRef callback) {
|
||||
for (Bucket* p = m_pListHead; p; p = p->pListNext) {
|
||||
Variant ret;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&p->data)));
|
||||
TypedValue args[1];
|
||||
tvDup(&p->data, args + 0);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
throw_collection_modified();
|
||||
}
|
||||
@@ -2671,8 +2678,9 @@ Object c_StableMap::t_filter(CVarRef callback) {
|
||||
for (Bucket* p = m_pListHead; p; p = p->pListNext) {
|
||||
Variant ret;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&p->data)));
|
||||
TypedValue args[1];
|
||||
tvDup(&p->data, args + 0);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
throw_collection_modified();
|
||||
}
|
||||
@@ -3616,7 +3624,9 @@ Object c_Set::t_map(CVarRef callback) {
|
||||
}
|
||||
TypedValue* tv = &np.data;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(tv, ctx, CREATE_VECTOR1(tvAsCVarRef(&p.data)));
|
||||
TypedValue args[1];
|
||||
tvDup(&p.data, args + 0);
|
||||
g_vmContext->invokeFuncFew(tv, ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
tvRefcountedDecRef(tv);
|
||||
throw_collection_modified();
|
||||
@@ -3642,8 +3652,9 @@ Object c_Set::t_filter(CVarRef callback) {
|
||||
if (!p.validValue()) continue;
|
||||
Variant ret;
|
||||
int32_t version = m_version;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&p.data)));
|
||||
TypedValue args[1];
|
||||
tvDup(&p.data, args + 0);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
|
||||
if (UNLIKELY(version != m_version)) {
|
||||
throw_collection_modified();
|
||||
}
|
||||
@@ -4352,8 +4363,9 @@ Object c_Pair::t_map(CVarRef callback) {
|
||||
Object obj = vec = NEWOBJ(c_Vector)();
|
||||
vec->reserve(2);
|
||||
for (uint64_t i = 0; i < 2; ++i) {
|
||||
g_vmContext->invokeFunc(&vec->m_data[i], ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&getElms()[i])));
|
||||
TypedValue args[1];
|
||||
tvDup(&getElms()[i], args + 0);
|
||||
g_vmContext->invokeFuncFew(&vec->m_data[i], ctx, 1, args);
|
||||
++vec->m_size;
|
||||
}
|
||||
return obj;
|
||||
@@ -4371,8 +4383,9 @@ Object c_Pair::t_filter(CVarRef callback) {
|
||||
Object obj = vec = NEWOBJ(c_Vector)();
|
||||
for (uint64_t i = 0; i < 2; ++i) {
|
||||
Variant ret;
|
||||
g_vmContext->invokeFunc(ret.asTypedValue(), ctx,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&getElms()[i])));
|
||||
TypedValue args[1];
|
||||
tvDup(&getElms()[i], args + 0);
|
||||
g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 1, args);
|
||||
if (ret.toBoolean()) {
|
||||
vec->add(&getElms()[i]);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ void c_Continuation::t_next() {
|
||||
|
||||
static StaticString s_next("next");
|
||||
void c_Continuation::t_rewind() {
|
||||
this->o_invoke(s_next, Array());
|
||||
this->o_invoke_few_args(s_next, 0);
|
||||
}
|
||||
|
||||
bool c_Continuation::t_valid() {
|
||||
|
||||
@@ -170,10 +170,10 @@ void c_UConverter::ucnvToUCallback(c_UConverter *objval,
|
||||
UConverterCallbackReason reason,
|
||||
UErrorCode *pErrorCode) {
|
||||
String source(args->source, args->sourceLimit - args->source, CopyString);
|
||||
VRefParam errRef((long)*pErrorCode);
|
||||
Variant ret = objval->o_invoke(s_toUCallback, CREATE_VECTOR4(
|
||||
reason, source, String(codeUnits, length, CopyString), ref(errRef)
|
||||
), -1);
|
||||
Variant errRef((int64_t)*pErrorCode);
|
||||
Variant ret = objval->o_invoke_few_args(
|
||||
s_toUCallback, 4,
|
||||
reason, source, String(codeUnits, length, CopyString), strongBind(errRef));
|
||||
if (errRef.is(KindOfInt64)) {
|
||||
*pErrorCode = (UErrorCode)errRef.toInt64();
|
||||
} else {
|
||||
@@ -229,10 +229,11 @@ void c_UConverter::ucnvFromUCallback(c_UConverter *objval,
|
||||
U16_NEXT(codeUnits, i, length, c);
|
||||
source.append((int64_t)c);
|
||||
}
|
||||
VRefParam errRef((int64_t)*pErrorCode);
|
||||
Variant ret = objval->o_invoke(s_fromUCallback, CREATE_VECTOR4(
|
||||
reason, source, (int64_t)codePoint, ref(errRef)
|
||||
), -1);
|
||||
Variant errRef((int64_t)*pErrorCode);
|
||||
Variant ret =
|
||||
objval->o_invoke_few_args(
|
||||
s_fromUCallback, 4,
|
||||
reason, source, (int64_t)codePoint, strongBind(errRef));
|
||||
if (errRef.is(KindOfInt64)) {
|
||||
*pErrorCode = (UErrorCode)errRef.toInt64();
|
||||
} else {
|
||||
|
||||
@@ -1833,7 +1833,7 @@ static bool do_fetch(sp_PDOStatement stmt, bool do_bind, Variant &ret,
|
||||
if (!stmt->fetch.constructor.empty() &&
|
||||
(flags & PDO_FETCH_PROPS_LATE)) {
|
||||
ret.asCObjRef().get()->o_invoke(stmt->fetch.constructor,
|
||||
stmt->fetch.ctor_args, -1);
|
||||
stmt->fetch.ctor_args);
|
||||
ret.asCObjRef().get()->clearNoDestruct();
|
||||
}
|
||||
}
|
||||
@@ -1969,8 +1969,7 @@ static bool do_fetch(sp_PDOStatement stmt, bool do_bind, Variant &ret,
|
||||
case PDO_FETCH_CLASS:
|
||||
if (!stmt->fetch.constructor.empty() &&
|
||||
!(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
|
||||
ret.toObject()->o_invoke(stmt->fetch.constructor, stmt->fetch.ctor_args,
|
||||
-1);
|
||||
ret.toObject()->o_invoke(stmt->fetch.constructor, stmt->fetch.ctor_args);
|
||||
ret.toObject()->clearNoDestruct();
|
||||
}
|
||||
if (flags & PDO_FETCH_CLASSTYPE) {
|
||||
|
||||
@@ -875,7 +875,7 @@ Variant f_hphp_invoke_method(CVarRef obj, CStrRef cls, CStrRef name,
|
||||
return invoke_static_method(cls, name, params);
|
||||
}
|
||||
ObjectData *o = obj.toCObjRef().get();
|
||||
return o->o_invoke(name, params, -1);
|
||||
return o->o_invoke(name, params);
|
||||
}
|
||||
|
||||
bool f_hphp_instanceof(CObjRef obj, CStrRef name) {
|
||||
|
||||
@@ -475,7 +475,7 @@ static bool do_request(c_SoapClient *client, xmlDoc *request,
|
||||
if (client->m_trace) {
|
||||
client->m_last_request = String((char*)buf, buf_size, CopyString);
|
||||
}
|
||||
response = client->o_invoke_few_args(s___dorequest, -1, 5,
|
||||
response = client->o_invoke_few_args(s___dorequest, 5,
|
||||
String(buf, buf_size, AttachLiteral),
|
||||
String(location, AttachLiteral),
|
||||
String(action, AttachLiteral),
|
||||
|
||||
@@ -207,14 +207,14 @@ Variant f_iterator_apply(CVarRef obj, CVarRef func,
|
||||
return false;
|
||||
}
|
||||
Object pobj = obj.toObject();
|
||||
pobj->o_invoke(s_rewind, null_array, -1);
|
||||
pobj->o_invoke_few_args(s_rewind, 0);
|
||||
int64_t count = 0;
|
||||
while (same(pobj->o_invoke(s_valid, null_array, -1), true)) {
|
||||
while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
|
||||
if (!same(vm_call_user_func(func, params), true)) {
|
||||
break;
|
||||
}
|
||||
++count;
|
||||
pobj->o_invoke(s_next, null_array, -1);
|
||||
pobj->o_invoke_few_args(s_next, 0);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -224,11 +224,11 @@ Variant f_iterator_count(CVarRef obj) {
|
||||
return false;
|
||||
}
|
||||
Object pobj = obj.toObject();
|
||||
pobj->o_invoke(s_rewind, null_array, -1);
|
||||
pobj->o_invoke_few_args(s_rewind, 0);
|
||||
int64_t count = 0;
|
||||
while (same(pobj->o_invoke(s_valid, null_array, -1), true)) {
|
||||
while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
|
||||
++count;
|
||||
pobj->o_invoke(s_next, null_array, -1);
|
||||
pobj->o_invoke_few_args(s_next, 0);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -239,16 +239,16 @@ Variant f_iterator_to_array(CVarRef obj, bool use_keys /* = true */) {
|
||||
}
|
||||
Array ret(Array::Create());
|
||||
Object pobj = obj.toObject();
|
||||
pobj->o_invoke(s_rewind, null_array, -1);
|
||||
while (same(pobj->o_invoke(s_valid, null_array, -1), true)) {
|
||||
Variant val = pobj->o_invoke(s_current, null_array, -1);
|
||||
pobj->o_invoke_few_args(s_rewind, 0);
|
||||
while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
|
||||
Variant val = pobj->o_invoke_few_args(s_current, 0);
|
||||
if (use_keys) {
|
||||
Variant key = pobj->o_invoke(s_key, null_array, -1);
|
||||
Variant key = pobj->o_invoke_few_args(s_key, 0);
|
||||
ret.set(key, val);
|
||||
} else {
|
||||
ret.append(val);
|
||||
}
|
||||
pobj->o_invoke(s_next, null_array, -1);
|
||||
pobj->o_invoke_few_args(s_next, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ static Variant xml_call_handler(XmlParser *parser, CVarRef handler,
|
||||
retval = invoke(handler.toString().c_str(), args, -1);
|
||||
} else {
|
||||
retval = parser->object.toObject()->
|
||||
o_invoke(handler.toString(), args, -1);
|
||||
o_invoke(handler.toString(), args);
|
||||
}
|
||||
} else if (handler.isArray() && handler.getArrayData()->size() == 2 &&
|
||||
(handler[0].isString() || handler[0].isObject()) &&
|
||||
|
||||
@@ -116,7 +116,7 @@ protected:
|
||||
buffer_used = 0;
|
||||
buffer_size = _buffer_size;
|
||||
p = _p;
|
||||
t = p->o_invoke(s_getTransport, Array(), -1);
|
||||
t = p->o_invoke_few_args(s_getTransport, 0);
|
||||
}
|
||||
~PHPTransport() {
|
||||
free(buffer);
|
||||
@@ -134,7 +134,7 @@ protected:
|
||||
|
||||
class PHPOutputTransport : public PHPTransport {
|
||||
public:
|
||||
PHPOutputTransport(CObjRef _p, size_t _buffer_size = 8192) {
|
||||
explicit PHPOutputTransport(CObjRef _p, size_t _buffer_size = 8192) {
|
||||
construct_with_zval(_p, _buffer_size);
|
||||
}
|
||||
|
||||
@@ -204,17 +204,16 @@ public:
|
||||
|
||||
protected:
|
||||
void directFlush() {
|
||||
t->o_invoke(s_flush, Array(), -1);
|
||||
t->o_invoke_few_args(s_flush, 0);
|
||||
}
|
||||
void directWrite(const char* data, size_t len) {
|
||||
Array args = CREATE_VECTOR1(String(data, len, CopyString));
|
||||
t->o_invoke(s_write, args, -1);
|
||||
t->o_invoke_few_args(s_write, 1, String(data, len, CopyString));
|
||||
}
|
||||
};
|
||||
|
||||
class PHPInputTransport : public PHPTransport {
|
||||
public:
|
||||
PHPInputTransport(Object _p, size_t _buffer_size = 8192) {
|
||||
explicit PHPInputTransport(Object _p, size_t _buffer_size = 8192) {
|
||||
construct_with_zval(_p, _buffer_size);
|
||||
}
|
||||
|
||||
@@ -236,9 +235,8 @@ public:
|
||||
|
||||
void put_back() {
|
||||
if (buffer_used) {
|
||||
t->o_invoke(s_putBack,
|
||||
CREATE_VECTOR1(String(buffer_ptr, buffer_used, CopyString)),
|
||||
-1);
|
||||
t->o_invoke_few_args(s_putBack,
|
||||
1, String(buffer_ptr, buffer_used, CopyString));
|
||||
}
|
||||
buffer_used = 0;
|
||||
buffer_ptr = buffer;
|
||||
@@ -299,8 +297,7 @@ public:
|
||||
protected:
|
||||
void refill() {
|
||||
assert(buffer_used == 0);
|
||||
String ret = t->o_invoke(s_read,
|
||||
CREATE_VECTOR1((int64_t)buffer_size), -1).toString();
|
||||
String ret = t->o_invoke_few_args(s_read, 1, (int64_t)buffer_size);
|
||||
buffer_used = ret.size();
|
||||
memcpy(buffer, ret.data(), buffer_used);
|
||||
buffer_ptr = buffer;
|
||||
|
||||
+243
-180
@@ -107,6 +107,16 @@ struct VMPrepareUnwind : std::exception {
|
||||
|
||||
}
|
||||
|
||||
ActRec* ActRec::arGetSfp() const {
|
||||
ActRec* prevFrame = (ActRec*)m_savedRbp;
|
||||
if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >=
|
||||
Util::s_stackSize)) {
|
||||
if (LIKELY(prevFrame != nullptr)) return prevFrame;
|
||||
}
|
||||
|
||||
return const_cast<ActRec*>(this);
|
||||
}
|
||||
|
||||
bool
|
||||
ActRec::skipFrame() const {
|
||||
return m_func && m_func->isBuiltin();
|
||||
@@ -327,8 +337,8 @@ void VarEnv::attach(ActRec* fp) {
|
||||
this,
|
||||
isGlobalScope() ? "global scope" : "local scope",
|
||||
int(fp->m_func->numNamedLocals()), fp);
|
||||
assert(m_depth == 0 || g_vmContext->arGetSfp(fp) == m_cfp ||
|
||||
(g_vmContext->arGetSfp(fp) == fp && g_vmContext->isNested()));
|
||||
assert(m_depth == 0 || fp->arGetSfp() == m_cfp ||
|
||||
(fp->arGetSfp() == fp && g_vmContext->isNested()));
|
||||
m_cfp = fp;
|
||||
m_depth++;
|
||||
|
||||
@@ -1105,7 +1115,7 @@ UnwindStatus Stack::unwindFrame(ActRec*& fp, int offset, PC& pc, Fault fault) {
|
||||
return UnwindResumeVM;
|
||||
}
|
||||
|
||||
ActRec *prevFp = context->arGetSfp(fp);
|
||||
ActRec *prevFp = fp->arGetSfp();
|
||||
SKTRACE(1, sk, "unwindFrame: fp %p prevFp %p\n",
|
||||
fp, prevFp);
|
||||
if (LIKELY(!fp->m_func->isGenerator())) {
|
||||
@@ -1156,7 +1166,7 @@ TypedValue* Stack::frameStackBase(const ActRec* fp) {
|
||||
TypedValue* Stack::generatorStackBase(const ActRec* fp) {
|
||||
assert(fp->m_func->isGenerator());
|
||||
VMExecutionContext* context = g_vmContext;
|
||||
ActRec* sfp = context->arGetSfp(fp);
|
||||
ActRec* sfp = fp->arGetSfp();
|
||||
if (sfp == fp) {
|
||||
// In the reentrant case, we can consult the savedVM state. We simply
|
||||
// use the top of stack of the previous VM frame (since the ActRec,
|
||||
@@ -1180,16 +1190,6 @@ __thread VarEnvArenaStorage s_varEnvArenaStorage;
|
||||
using namespace HPHP::VM;
|
||||
using namespace HPHP::MethodLookup;
|
||||
|
||||
ActRec* VMExecutionContext::arGetSfp(const ActRec* ar) {
|
||||
ActRec* prevFrame = (ActRec*)ar->m_savedRbp;
|
||||
if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >=
|
||||
Util::s_stackSize)) {
|
||||
if (LIKELY(prevFrame != nullptr)) return prevFrame;
|
||||
}
|
||||
|
||||
return const_cast<ActRec*>(ar);
|
||||
}
|
||||
|
||||
ActRec* VMExecutionContext::getOuterVMFrame(const ActRec* ar) {
|
||||
ActRec* prevFrame = (ActRec*)ar->m_savedRbp;
|
||||
if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >=
|
||||
@@ -1698,65 +1698,62 @@ static inline void checkStack(Stack& stk, const Func* f) {
|
||||
}
|
||||
}
|
||||
|
||||
template <bool reenter>
|
||||
bool VMExecutionContext::prepareFuncEntry(ActRec *ar,
|
||||
PC& pc,
|
||||
ExtraArgs* extraArgs) {
|
||||
bool VMExecutionContext::prepareFuncEntry(ActRec *ar, PC& pc) {
|
||||
const Func* func = ar->m_func;
|
||||
if (!reenter) {
|
||||
// For the reenter case, magic shuffling are handled
|
||||
// in invokeFunc() before calling prepareFuncEntry().
|
||||
if (UNLIKELY(ar->hasInvName())) {
|
||||
shuffleMagicArgs(ar);
|
||||
}
|
||||
}
|
||||
// It is now safe to access m_varEnv directly
|
||||
assert(!ar->hasInvName());
|
||||
int nargs = ar->numArgs();
|
||||
// Set pc below, once we know that DV dispatch is unnecessary.
|
||||
m_fp = ar;
|
||||
Offset firstDVInitializer = InvalidAbsoluteOffset;
|
||||
bool raiseMissingArgumentWarnings = false;
|
||||
int nparams = func->numParams();
|
||||
Offset firstDVInitializer = InvalidAbsoluteOffset;
|
||||
if (nargs != nparams) {
|
||||
if (nargs < nparams) {
|
||||
// Push uninitialized nulls for missing arguments. Some of them may end
|
||||
// up getting default-initialized, but regardless, we need to make space
|
||||
// for them on the stack.
|
||||
const Func::ParamInfoVec& paramInfo = func->params();
|
||||
for (int i = nargs; i < nparams; ++i) {
|
||||
m_stack.pushUninit();
|
||||
Offset dvInitializer = paramInfo[i].funcletOff();
|
||||
if (dvInitializer == InvalidAbsoluteOffset) {
|
||||
// We wait to raise warnings until after all the locals have been
|
||||
// initialized. This is important because things need to be in a
|
||||
// consistent state in case the user error handler throws.
|
||||
raiseMissingArgumentWarnings = true;
|
||||
} else if (firstDVInitializer == InvalidAbsoluteOffset) {
|
||||
// This is the first unpassed arg with a default value, so
|
||||
// this is where we'll need to jump to.
|
||||
firstDVInitializer = dvInitializer;
|
||||
}
|
||||
if (UNLIKELY(ar->m_varEnv != nullptr)) {
|
||||
/*
|
||||
* m_varEnv != nullptr => we have a varEnv, extraArgs, or an invName.
|
||||
*/
|
||||
if (ar->hasInvName()) {
|
||||
// shuffleMagicArgs deals with everything. no need for
|
||||
// further argument munging
|
||||
shuffleMagicArgs(ar);
|
||||
} else if (ar->hasVarEnv()) {
|
||||
m_fp = ar;
|
||||
if (!func->isGenerator()) {
|
||||
assert(func->isPseudoMain());
|
||||
pushLocalsAndIterators(func);
|
||||
ar->m_varEnv->attach(ar);
|
||||
}
|
||||
assert(m_fp->m_func == func);
|
||||
pc = func->getEntry();
|
||||
// Nothing more to do; get out
|
||||
return true;
|
||||
} else {
|
||||
// For the reenter case, extra arguments are handled below (with
|
||||
// the extraArgs vector passed to this function). The below
|
||||
// handles pulling extra args from the execution stack in a
|
||||
// non-reentry case.
|
||||
if (!reenter) {
|
||||
assert(ar->hasExtraArgs());
|
||||
assert(func->numParams() < ar->numArgs());
|
||||
}
|
||||
} else {
|
||||
int nargs = ar->numArgs();
|
||||
if (nargs != nparams) {
|
||||
if (nargs < nparams) {
|
||||
// Push uninitialized nulls for missing arguments. Some of them may end
|
||||
// up getting default-initialized, but regardless, we need to make space
|
||||
// for them on the stack.
|
||||
const Func::ParamInfoVec& paramInfo = func->params();
|
||||
for (int i = nargs; i < nparams; ++i) {
|
||||
m_stack.pushUninit();
|
||||
Offset dvInitializer = paramInfo[i].funcletOff();
|
||||
if (dvInitializer == InvalidAbsoluteOffset) {
|
||||
// We wait to raise warnings until after all the locals have been
|
||||
// initialized. This is important because things need to be in a
|
||||
// consistent state in case the user error handler throws.
|
||||
raiseMissingArgumentWarnings = true;
|
||||
} else if (firstDVInitializer == InvalidAbsoluteOffset) {
|
||||
// This is the first unpassed arg with a default value, so
|
||||
// this is where we'll need to jump to.
|
||||
firstDVInitializer = dvInitializer;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (func->attrs() & AttrMayUseVV) {
|
||||
// If there are extra parameters then we cannot be a pseudomain
|
||||
// inheriting a VarEnv
|
||||
assert(!m_fp->m_varEnv);
|
||||
// Extra parameters must be moved off the stack.
|
||||
const int numExtras = nargs - nparams;
|
||||
m_fp->setExtraArgs(ExtraArgs::allocateCopy(
|
||||
(TypedValue*)(uintptr_t(m_fp) - nargs * sizeof(TypedValue)),
|
||||
numExtras));
|
||||
for (int i = 0; i < numExtras; i++) {
|
||||
m_stack.discard();
|
||||
}
|
||||
ar->setExtraArgs(ExtraArgs::allocateCopy((TypedValue*)ar - nargs,
|
||||
numExtras));
|
||||
m_stack.ndiscard(numExtras);
|
||||
} else {
|
||||
// The function we're calling is not marked as "MayUseVV",
|
||||
// so just discard the extra arguments
|
||||
@@ -1788,37 +1785,20 @@ bool VMExecutionContext::prepareFuncEntry(ActRec *ar,
|
||||
pushLocalsAndIterators(func, nlocals);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're reentering, make sure to finalize the ActRec before
|
||||
* possibly raising any exceptions, so unwinding won't get confused.
|
||||
*/
|
||||
if (reenter) {
|
||||
if (ar->hasVarEnv()) {
|
||||
// If this is a pseudomain inheriting a VarEnv from our caller,
|
||||
// there cannot be extra arguments
|
||||
assert(!extraArgs);
|
||||
// Now that locals have been initialized, it is safe to attach
|
||||
// the VarEnv inherited from our caller to the current frame
|
||||
ar->m_varEnv->attach(ar);
|
||||
} else if (extraArgs) {
|
||||
// Create an ExtraArgs structure and stash the extra args in
|
||||
// there.
|
||||
ar->setExtraArgs(extraArgs);
|
||||
}
|
||||
m_fp = ar;
|
||||
if (firstDVInitializer != InvalidAbsoluteOffset) {
|
||||
pc = func->unit()->entry() + firstDVInitializer;
|
||||
} else {
|
||||
pc = func->getEntry();
|
||||
}
|
||||
|
||||
// cppext functions/methods have their own logic for raising
|
||||
// warnings for missing arguments, so we only need to do this work
|
||||
// for non-cppext functions/methods
|
||||
if (raiseMissingArgumentWarnings && !func->isBuiltin()) {
|
||||
pc = func->getEntry();
|
||||
// m_pc is not set to callee. if the callee is in a different unit,
|
||||
// debugBacktrace() can barf in unit->offsetOf(m_pc) where it
|
||||
// asserts that m_pc >= m_bc && m_pc < m_bc + m_bclen. Sync m_fp
|
||||
// to function entry point in called unit.
|
||||
// need to sync m_pc to pc for backtraces/re-entry
|
||||
SYNC();
|
||||
const Func::ParamInfoVec& paramInfo = func->params();
|
||||
for (int i = nargs; i < nparams; ++i) {
|
||||
for (int i = ar->numArgs(); i < nparams; ++i) {
|
||||
Offset dvInitializer = paramInfo[i].funcletOff();
|
||||
if (dvInitializer == InvalidAbsoluteOffset) {
|
||||
const char* name = func->name()->data();
|
||||
@@ -1830,12 +1810,6 @@ bool VMExecutionContext::prepareFuncEntry(ActRec *ar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstDVInitializer != InvalidAbsoluteOffset) {
|
||||
pc = func->unit()->entry() + firstDVInitializer;
|
||||
} else {
|
||||
pc = func->getEntry();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1845,17 +1819,39 @@ void VMExecutionContext::syncGdbState() {
|
||||
}
|
||||
}
|
||||
|
||||
void VMExecutionContext::enterVMPrologue(ActRec* enterFnAr) {
|
||||
assert(enterFnAr);
|
||||
Stats::inc(Stats::VMEnter);
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getJit()) {
|
||||
int np = enterFnAr->m_func->numParams();
|
||||
int na = enterFnAr->numArgs();
|
||||
if (na > np) na = np + 1;
|
||||
TCA start = enterFnAr->m_func->getPrologue(na);
|
||||
tx64->enterTCAtProlog(enterFnAr, start);
|
||||
} else {
|
||||
if (prepareFuncEntry(enterFnAr, m_pc)) {
|
||||
enterVMWork(enterFnAr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VMExecutionContext::enterVMWork(ActRec* enterFnAr) {
|
||||
TCA start = nullptr;
|
||||
if (enterFnAr) {
|
||||
if (!EventHook::FunctionEnter(enterFnAr, EventHook::NormalFunc)) return;
|
||||
checkStack(m_stack, enterFnAr->m_func);
|
||||
start = enterFnAr->m_func->getFuncBody();
|
||||
}
|
||||
Stats::inc(Stats::VMEnter);
|
||||
if (LIKELY(ThreadInfo::s_threadInfo->m_reqInjectionData.getJit())) {
|
||||
Transl::SrcKey sk(curFunc(), m_pc);
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getJit()) {
|
||||
(void) curUnit()->offsetOf(m_pc); /* assert */
|
||||
tx64->enterTC(sk, start);
|
||||
if (enterFnAr) {
|
||||
assert(start);
|
||||
tx64->enterTCAfterProlog(start);
|
||||
} else {
|
||||
Transl::SrcKey sk(curFunc(), m_pc);
|
||||
tx64->enterTCAtSrcKey(sk);
|
||||
}
|
||||
} else {
|
||||
dispatch();
|
||||
}
|
||||
@@ -1912,9 +1908,7 @@ static int exception_handler() {
|
||||
return longJmpType;
|
||||
}
|
||||
|
||||
void VMExecutionContext::enterVM(TypedValue* retval,
|
||||
ActRec* ar,
|
||||
ExtraArgs* extraArgs) {
|
||||
void VMExecutionContext::enterVM(TypedValue* retval, ActRec* ar) {
|
||||
m_firstAR = ar;
|
||||
ar->m_savedRip = (uintptr_t)tx64->getCallToExit();
|
||||
assert(isReturnHelper(ar->m_savedRip));
|
||||
@@ -1945,8 +1939,12 @@ short_jump:
|
||||
try {
|
||||
switch (jumpCode) {
|
||||
case EXCEPTION_START:
|
||||
if (prepareFuncEntry<true>(ar, m_pc, extraArgs)) {
|
||||
enterVMWork(ar);
|
||||
if (m_fp && !ar->m_varEnv) {
|
||||
enterVMPrologue(ar);
|
||||
} else {
|
||||
if (prepareFuncEntry(ar, m_pc)) {
|
||||
enterVMWork(ar);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCEPTION_PROPAGATE:
|
||||
@@ -2007,7 +2005,6 @@ propagate:
|
||||
|
||||
void VMExecutionContext::reenterVM(TypedValue* retval,
|
||||
ActRec* ar,
|
||||
ExtraArgs* extraArgs,
|
||||
TypedValue* savedSP) {
|
||||
ar->m_soff = 0;
|
||||
ar->m_savedRbp = 0;
|
||||
@@ -2016,7 +2013,7 @@ void VMExecutionContext::reenterVM(TypedValue* retval,
|
||||
pushVMState(savedVM, ar);
|
||||
assert(m_nestedVMs.size() >= 1);
|
||||
try {
|
||||
enterVM(retval, ar, extraArgs);
|
||||
enterVM(retval, ar);
|
||||
popVMState();
|
||||
} catch (...) {
|
||||
popVMState();
|
||||
@@ -2069,7 +2066,9 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
|
||||
}
|
||||
Cell* savedSP = m_stack.top();
|
||||
|
||||
checkStack(m_stack, f);
|
||||
if (f->numParams() > kStackCheckReenterPadding - kNumActRecCells) {
|
||||
checkStack(m_stack, f);
|
||||
}
|
||||
|
||||
if (flags & InvokePseudoMain) {
|
||||
assert(f->isPseudoMain() && !params.get());
|
||||
@@ -2111,7 +2110,6 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
|
||||
#endif
|
||||
|
||||
ArrayData *arr = params.get();
|
||||
ExtraArgs* extraArgs = nullptr;
|
||||
if (isMagicCall) {
|
||||
// Put the method name into the location of the first parameter. We
|
||||
// are transferring ownership, so no need to incRef/decRef here.
|
||||
@@ -2121,8 +2119,10 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
|
||||
} else if (arr) {
|
||||
const int numParams = f->numParams();
|
||||
const int numExtraArgs = arr->size() - numParams;
|
||||
ExtraArgs* extraArgs = nullptr;
|
||||
if (numExtraArgs > 0 && (f->attrs() & AttrMayUseVV)) {
|
||||
extraArgs = ExtraArgs::allocateUninit(numExtraArgs);
|
||||
ar->setExtraArgs(extraArgs);
|
||||
}
|
||||
int paramId = 0;
|
||||
for (ssize_t i = arr->iter_begin();
|
||||
@@ -2160,6 +2160,7 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
|
||||
if (extraArgs) {
|
||||
int n = paramId >= numParams ? paramId - numParams + 1 : 0;
|
||||
ExtraArgs::deallocate(extraArgs, n);
|
||||
ar->m_varEnv = nullptr;
|
||||
paramId -= n;
|
||||
}
|
||||
while (paramId >= 0) {
|
||||
@@ -2175,10 +2176,73 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
|
||||
}
|
||||
|
||||
if (m_fp) {
|
||||
reenterVM(retval, ar, extraArgs, savedSP);
|
||||
reenterVM(retval, ar, savedSP);
|
||||
} else {
|
||||
assert(m_nestedVMs.size() == 0);
|
||||
enterVM(retval, ar, extraArgs);
|
||||
enterVM(retval, ar);
|
||||
}
|
||||
}
|
||||
|
||||
void VMExecutionContext::invokeFuncFew(TypedValue* retval,
|
||||
const Func* f,
|
||||
void* thisOrCls,
|
||||
StringData* invName,
|
||||
int argc, TypedValue* argv) {
|
||||
assert(retval);
|
||||
assert(f);
|
||||
// If this is a regular function, this_ and cls must be NULL
|
||||
assert(f->preClass() || !thisOrCls);
|
||||
// If this is a method, either this_ or cls must be non-NULL
|
||||
assert(!f->preClass() || thisOrCls);
|
||||
// If this is a static method, this_ must be NULL
|
||||
assert(!(f->attrs() & AttrStatic && !f->isClosureBody()) ||
|
||||
!ActRec::decodeThis(thisOrCls));
|
||||
// invName should only be non-NULL if we are calling __call or
|
||||
// __callStatic
|
||||
assert(!invName || f->name()->isame(s___call.get()) ||
|
||||
f->name()->isame(s___callStatic.get()));
|
||||
|
||||
VMRegAnchor _;
|
||||
|
||||
if (ObjectData* thiz = ActRec::decodeThis(thisOrCls)) {
|
||||
thiz->incRefCount();
|
||||
}
|
||||
Cell* savedSP = m_stack.top();
|
||||
if (argc > kStackCheckReenterPadding - kNumActRecCells) {
|
||||
checkStack(m_stack, f);
|
||||
}
|
||||
ActRec* ar = m_stack.allocA();
|
||||
ar->m_soff = 0;
|
||||
ar->m_savedRbp = 0;
|
||||
ar->m_func = f;
|
||||
ar->m_this = (ObjectData*)thisOrCls;
|
||||
ar->initNumArgs(argc);
|
||||
if (UNLIKELY(invName != nullptr)) {
|
||||
ar->setInvName(invName);
|
||||
} else {
|
||||
ar->m_varEnv = nullptr;
|
||||
}
|
||||
|
||||
#ifdef HPHP_TRACE
|
||||
if (m_fp == nullptr) {
|
||||
TRACE(1, "Reentry: enter %s(%p) from top-level\n",
|
||||
f->name()->data(), ar);
|
||||
} else {
|
||||
TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
|
||||
f->name()->data(), m_pc, ar,
|
||||
m_fp->m_func ? m_fp->m_func->name()->data() : "unknownBuiltin", m_fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
*m_stack.allocTV() = *argv++;
|
||||
}
|
||||
|
||||
if (m_fp) {
|
||||
reenterVM(retval, ar, savedSP);
|
||||
} else {
|
||||
assert(m_nestedVMs.size() == 0);
|
||||
enterVM(retval, ar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2193,7 +2257,9 @@ void VMExecutionContext::invokeContFunc(const Func* f,
|
||||
this_->incRefCount();
|
||||
|
||||
Cell* savedSP = m_stack.top();
|
||||
checkStack(m_stack, f);
|
||||
|
||||
// no need to check stack due to ReenterPadding
|
||||
assert(kStackCheckReenterPadding - kNumActRecCells >= 1);
|
||||
|
||||
ActRec* ar = m_stack.allocA();
|
||||
ar->m_savedRbp = 0;
|
||||
@@ -2208,7 +2274,7 @@ void VMExecutionContext::invokeContFunc(const Func* f,
|
||||
}
|
||||
|
||||
TypedValue retval;
|
||||
reenterVM(&retval, ar, nullptr, savedSP);
|
||||
reenterVM(&retval, ar, savedSP);
|
||||
// Codegen for generator functions guarantees that they will return null
|
||||
assert(IS_NULL_TYPE(retval.m_type));
|
||||
}
|
||||
@@ -2266,7 +2332,7 @@ ActRec* VMExecutionContext::getPrevVMState(const ActRec* fp,
|
||||
if (fp == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
ActRec* prevFp = arGetSfp(fp);
|
||||
ActRec* prevFp = fp->arGetSfp();
|
||||
if (prevFp != fp) {
|
||||
if (prevSp) {
|
||||
if (UNLIKELY(fp->m_func->isGenerator())) {
|
||||
@@ -4532,7 +4598,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopRetC(PC& pc) {
|
||||
|
||||
// Call the runtime helpers to free the local variables and iterators
|
||||
frame_free_locals_inl(m_fp, m_fp->m_func->numLocals());
|
||||
ActRec* sfp = arGetSfp(m_fp);
|
||||
ActRec* sfp = m_fp->arGetSfp();
|
||||
// Memcpy the the return value on top of the activation record. This works
|
||||
// the same regardless of whether the return value is boxed or not.
|
||||
TypedValue* retval_ptr = &m_fp->m_r;
|
||||
@@ -5982,7 +6048,7 @@ void VMExecutionContext::iopFPassM(PC& pc) {
|
||||
}
|
||||
|
||||
void VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
|
||||
assert(ar->m_savedRbp == (uint64_t)m_fp);
|
||||
assert(getOuterVMFrame(ar) == m_fp);
|
||||
ar->m_savedRip = (uintptr_t)tx64->getRetFromInterpretedFrame();
|
||||
assert(isReturnHelper(ar->m_savedRip));
|
||||
TRACE(3, "FCall: pc %p func %p base %d\n", m_pc,
|
||||
@@ -5990,8 +6056,8 @@ void VMExecutionContext::doFCall(ActRec* ar, PC& pc) {
|
||||
int(m_fp->m_func->base()));
|
||||
ar->m_soff = m_fp->m_func->unit()->offsetOf(pc)
|
||||
- (uintptr_t)m_fp->m_func->base();
|
||||
assert(pcOff() > m_fp->m_func->base());
|
||||
prepareFuncEntry<false>(ar, pc, 0);
|
||||
assert(pcOff() >= m_fp->m_func->base());
|
||||
prepareFuncEntry(ar, pc);
|
||||
SYNC();
|
||||
if (!EventHook::FunctionEnter(ar, EventHook::NormalFunc)) {
|
||||
pc = m_pc;
|
||||
@@ -6146,81 +6212,82 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFCallBuiltin(PC& pc) {
|
||||
}
|
||||
|
||||
bool VMExecutionContext::prepareArrayArgs(ActRec* ar,
|
||||
ArrayData* args,
|
||||
ExtraArgs*& extraArgs) {
|
||||
extraArgs = nullptr;
|
||||
ArrayData* args) {
|
||||
if (UNLIKELY(ar->hasInvName())) {
|
||||
m_stack.pushStringNoRc(ar->getInvName());
|
||||
m_stack.pushArray(args);
|
||||
ar->setVarEnv(0);
|
||||
ar->initNumArgs(2);
|
||||
} else {
|
||||
int nargs = args->size();
|
||||
const Func* f = ar->m_func;
|
||||
int nparams = f->numParams();
|
||||
int extra = nargs - nparams;
|
||||
if (extra < 0) {
|
||||
extra = 0;
|
||||
nparams = nargs;
|
||||
return true;
|
||||
}
|
||||
|
||||
int nargs = args->size();
|
||||
const Func* f = ar->m_func;
|
||||
int nparams = f->numParams();
|
||||
int extra = nargs - nparams;
|
||||
if (extra < 0) {
|
||||
extra = 0;
|
||||
nparams = nargs;
|
||||
}
|
||||
ssize_t pos = args->iter_begin();
|
||||
for (int i = 0; i < nparams; ++i) {
|
||||
TypedValue* from = const_cast<TypedValue*>(
|
||||
args->getValueRef(pos).asTypedValue());
|
||||
TypedValue* to = m_stack.allocTV();
|
||||
if (UNLIKELY(f->byRef(i))) {
|
||||
if (UNLIKELY(!tvAsVariant(from).isReferenced())) {
|
||||
raise_warning("Parameter %d to %s() expected to be a reference, "
|
||||
"value given", i + 1, f->fullName()->data());
|
||||
if (skipCufOnInvalidParams) {
|
||||
m_stack.discard();
|
||||
while (i--) m_stack.popTV();
|
||||
m_stack.popAR();
|
||||
m_stack.pushNull();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
tvDup(from, to);
|
||||
} else {
|
||||
tvDup(from, to);
|
||||
if (UNLIKELY(to->m_type == KindOfRef)) {
|
||||
tvUnbox(to);
|
||||
}
|
||||
}
|
||||
ssize_t pos = args->iter_begin();
|
||||
for (int i = 0; i < nparams; ++i) {
|
||||
TypedValue* from = const_cast<TypedValue*>(
|
||||
args->getValueRef(pos).asTypedValue());
|
||||
TypedValue* to = m_stack.allocTV();
|
||||
if (UNLIKELY(f->byRef(i))) {
|
||||
if (UNLIKELY(!tvAsVariant(from).isReferenced())) {
|
||||
raise_warning("Parameter %d to %s() expected to be a reference, "
|
||||
"value given", i + 1, f->fullName()->data());
|
||||
if (skipCufOnInvalidParams) {
|
||||
m_stack.discard();
|
||||
while (i--) m_stack.popTV();
|
||||
m_stack.popAR();
|
||||
m_stack.pushNull();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
tvDup(from, to);
|
||||
} else {
|
||||
tvDup(from, to);
|
||||
if (UNLIKELY(to->m_type == KindOfRef)) {
|
||||
tvUnbox(to);
|
||||
}
|
||||
pos = args->iter_advance(pos);
|
||||
}
|
||||
if (extra && (ar->m_func->attrs() & AttrMayUseVV)) {
|
||||
ExtraArgs* extraArgs = ExtraArgs::allocateUninit(extra);
|
||||
for (int i = 0; i < extra; ++i) {
|
||||
TypedValue* to = extraArgs->getExtraArg(i);
|
||||
tvDup(args->getValueRef(pos).asTypedValue(), to);
|
||||
if (to->m_type == KindOfRef && to->m_data.pref->_count == 2) {
|
||||
tvUnbox(to);
|
||||
}
|
||||
pos = args->iter_advance(pos);
|
||||
}
|
||||
if (extra && (ar->m_func->attrs() & AttrMayUseVV)) {
|
||||
extraArgs = ExtraArgs::allocateUninit(extra);
|
||||
for (int i = 0; i < extra; ++i) {
|
||||
TypedValue* to = extraArgs->getExtraArg(i);
|
||||
tvDup(args->getValueRef(pos).asTypedValue(), to);
|
||||
if (to->m_type == KindOfRef && to->m_data.pref->_count == 2) {
|
||||
tvUnbox(to);
|
||||
}
|
||||
pos = args->iter_advance(pos);
|
||||
}
|
||||
ar->initNumArgs(nargs);
|
||||
} else {
|
||||
ar->initNumArgs(nparams);
|
||||
}
|
||||
ar->setExtraArgs(extraArgs);
|
||||
ar->initNumArgs(nargs);
|
||||
} else {
|
||||
ar->initNumArgs(nparams);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cleanupParamsAndActRec(Stack& stack,
|
||||
ActRec* ar,
|
||||
ExtraArgs* extraArgs) {
|
||||
ActRec* ar,
|
||||
ExtraArgs* extraArgs) {
|
||||
assert(stack.top() + (extraArgs ?
|
||||
ar->m_func->numParams() :
|
||||
ar->numArgs()) == (void*)ar);
|
||||
while (stack.top() != (void*)ar) {
|
||||
stack.popTV();
|
||||
}
|
||||
stack.popAR();
|
||||
if (extraArgs) {
|
||||
const int numExtra = ar->numArgs() - ar->m_func->numParams();
|
||||
ExtraArgs::deallocate(extraArgs, numExtra);
|
||||
}
|
||||
while (stack.top() != (void*)ar) {
|
||||
stack.popTV();
|
||||
}
|
||||
stack.popAR();
|
||||
}
|
||||
|
||||
bool VMExecutionContext::doFCallArray(PC& pc) {
|
||||
@@ -6240,7 +6307,6 @@ bool VMExecutionContext::doFCallArray(PC& pc) {
|
||||
}
|
||||
|
||||
const Func* func = ar->m_func;
|
||||
ExtraArgs* extraArgs = nullptr;
|
||||
{
|
||||
Array args(LIKELY(c1->m_type == KindOfArray) ? c1->m_data.parr :
|
||||
tvAsVariant(c1).toArray().get());
|
||||
@@ -6258,10 +6324,10 @@ bool VMExecutionContext::doFCallArray(PC& pc) {
|
||||
- (uintptr_t)m_fp->m_func->base();
|
||||
assert(pcOff() > m_fp->m_func->base());
|
||||
|
||||
if (UNLIKELY(!prepareArrayArgs(ar, args.get(), extraArgs))) return false;
|
||||
if (UNLIKELY(!prepareArrayArgs(ar, args.get()))) return false;
|
||||
}
|
||||
|
||||
if (UNLIKELY(!(prepareFuncEntry<true>(ar, pc, extraArgs)))) {
|
||||
if (UNLIKELY(!(prepareFuncEntry(ar, pc)))) {
|
||||
return false;
|
||||
}
|
||||
SYNC();
|
||||
@@ -6694,7 +6760,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopNativeImpl(PC& pc) {
|
||||
// Adjust the stack; the native implementation put the return value in the
|
||||
// right place for us already
|
||||
m_stack.ndiscard(m_fp->m_func->numSlotsInFrame());
|
||||
ActRec* sfp = arGetSfp(m_fp);
|
||||
ActRec* sfp = m_fp->arGetSfp();
|
||||
if (LIKELY(sfp != m_fp)) {
|
||||
// Restore caller's execution state.
|
||||
m_fp = sfp;
|
||||
@@ -6969,7 +7035,7 @@ void VMExecutionContext::iopContExit(PC& pc) {
|
||||
NEXT();
|
||||
|
||||
EventHook::FunctionExit(m_fp);
|
||||
ActRec* prevFp = arGetSfp(m_fp);
|
||||
ActRec* prevFp = m_fp->arGetSfp();
|
||||
pc = prevFp->m_func->getEntry() + m_fp->m_soff;
|
||||
m_fp = prevFp;
|
||||
}
|
||||
@@ -7029,7 +7095,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopContRetC(PC& pc) {
|
||||
m_stack.popC();
|
||||
|
||||
EventHook::FunctionExit(m_fp);
|
||||
ActRec* prevFp = arGetSfp(m_fp);
|
||||
ActRec* prevFp = m_fp->arGetSfp();
|
||||
pc = prevFp->m_func->getEntry() + m_fp->m_soff;
|
||||
m_fp = prevFp;
|
||||
}
|
||||
@@ -7428,16 +7494,13 @@ void VMExecutionContext::pushVMState(VMState &savedVM,
|
||||
void VMExecutionContext::popVMState() {
|
||||
assert(m_nestedVMs.size() >= 1);
|
||||
|
||||
VMState savedVM;
|
||||
memcpy(&savedVM, &m_nestedVMs.back(), sizeof(savedVM));
|
||||
VMState &savedVM = m_nestedVMs.back().m_savedState;
|
||||
m_pc = savedVM.pc;
|
||||
m_fp = savedVM.fp;
|
||||
m_firstAR = savedVM.firstAR;
|
||||
assert(m_stack.top() == savedVM.sp);
|
||||
|
||||
if (debug) {
|
||||
const ReentryRecord& rr = m_nestedVMs.back();
|
||||
const VMState& savedVM = rr.m_savedState;
|
||||
if (savedVM.fp &&
|
||||
savedVM.fp->m_func &&
|
||||
savedVM.fp->m_func->unit()) {
|
||||
|
||||
@@ -256,6 +256,10 @@ struct ActRec {
|
||||
};
|
||||
};
|
||||
|
||||
// Get the next outermost VM frame, but if this is
|
||||
// a re-entry frame, return ar
|
||||
ActRec* arGetSfp() const;
|
||||
|
||||
// skip this frame if it is for a builtin function
|
||||
bool skipFrame() const;
|
||||
|
||||
@@ -288,6 +292,22 @@ struct ActRec {
|
||||
initNumArgs(numArgs, isFromFPushCtor());
|
||||
}
|
||||
|
||||
static void* encodeThis(ObjectData* obj, VM::Class* cls) {
|
||||
if (obj) return obj;
|
||||
if (cls) return (char*)cls + 1;
|
||||
}
|
||||
|
||||
static void* encodeThis(ObjectData* obj) { return obj; }
|
||||
static void* encodeClass(const VM::Class* cls) {
|
||||
return cls ? (char*)cls + 1 : nullptr;
|
||||
}
|
||||
static ObjectData* decodeThis(void* p) {
|
||||
return uintptr_t(p) & 1 ? nullptr : (ObjectData*)p;
|
||||
}
|
||||
static VM::Class* decodeClass(void* p) {
|
||||
return uintptr_t(p) & 1 ? (VM::Class*)(uintptr_t(p)&~1LL) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* To conserve space, we use unions for pairs of mutually exclusive
|
||||
* fields (fields that are not used at the same time). We use unions
|
||||
@@ -419,15 +439,20 @@ constexpr size_t kNumIterCells = sizeof(Iter) / sizeof(Cell);
|
||||
constexpr size_t kNumActRecCells = sizeof(ActRec) / sizeof(Cell);
|
||||
|
||||
/*
|
||||
* We pad all stack overflow checks by a small amount to allow for two
|
||||
* We pad all stack overflow checks by a small amount to allow for three
|
||||
* things:
|
||||
*
|
||||
* - inlining functions without having to either do another stack
|
||||
* check (or chase down prologues to smash checks to be bigger).
|
||||
*
|
||||
* - omitting stack overflow checks on leaf functions
|
||||
*
|
||||
* - delaying stack overflow checks on reentry
|
||||
*/
|
||||
constexpr int kStackCheckPadding = 20;
|
||||
constexpr int kStackCheckLeafPadding = 20;
|
||||
constexpr int kStackCheckReenterPadding = 9;
|
||||
constexpr int kStackCheckPadding = kStackCheckLeafPadding +
|
||||
kStackCheckReenterPadding;
|
||||
|
||||
struct Fault {
|
||||
enum Type : int16_t {
|
||||
|
||||
@@ -1200,12 +1200,12 @@ TypedValue* Class::clsCnsGet(const StringData* clsCnsName) const {
|
||||
static StringData* sd86cinit = StringData::GetStaticString("86cinit");
|
||||
const Func* meth86cinit =
|
||||
m_constants[clsCnsInd].m_class->lookupMethod(sd86cinit);
|
||||
TypedValue tv;
|
||||
tv.m_data.pstr = (StringData*)clsCnsName;
|
||||
tv.m_type = KindOfString;
|
||||
g_vmContext->invokeFunc(clsCns, meth86cinit,
|
||||
CREATE_VECTOR1(tvAsCVarRef(&tv)), nullptr,
|
||||
const_cast<Class*>(this));
|
||||
TypedValue tv[1];
|
||||
tv->m_data.pstr = (StringData*)clsCnsName;
|
||||
tv->m_type = KindOfString;
|
||||
clsCnsName->incRefCount();
|
||||
g_vmContext->invokeFuncFew(clsCns, meth86cinit, ActRec::encodeClass(this),
|
||||
nullptr, 1, tv);
|
||||
}
|
||||
return clsCns;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ Instance* Instance::callCustomInstanceInit() {
|
||||
// reasonable refcount.
|
||||
try {
|
||||
incRefCount();
|
||||
g_vmContext->invokeFunc(&tv, init, null_array, this);
|
||||
g_vmContext->invokeFuncFew(&tv, init, this);
|
||||
decRefCount();
|
||||
assert(!IS_REFCOUNTED_TYPE(tv.m_type));
|
||||
} catch (...) {
|
||||
@@ -625,7 +625,7 @@ Variant Instance::t___destruct() {
|
||||
const Func* method = m_cls->lookupMethod(sd__destruct);
|
||||
if (method) {
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)&v, method, null_array, this);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this);
|
||||
return v;
|
||||
} else {
|
||||
return uninit_null();
|
||||
@@ -637,8 +637,10 @@ Variant Instance::t___call(Variant v_name, Variant v_arguments) {
|
||||
const Func* method = m_cls->lookupMethod(sd__call);
|
||||
if (method) {
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)&v, method,
|
||||
CREATE_VECTOR2(v_name, v_arguments), this);
|
||||
TypedValue args[2];
|
||||
tvDup(v_name.asTypedValue(), args + 0);
|
||||
tvDup(v_arguments.asTypedValue(), args + 1);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 2, args);
|
||||
return v;
|
||||
} else {
|
||||
return uninit_null();
|
||||
@@ -662,8 +664,9 @@ Variant Instance::t___get(Variant v_name) {
|
||||
const Func* method = m_cls->lookupMethod(s___get.get());
|
||||
if (method) {
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)&v, method,
|
||||
CREATE_VECTOR1(v_name), this);
|
||||
TypedValue args[1];
|
||||
tvDup(v_name.asTypedValue(), args + 0);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
|
||||
return v;
|
||||
} else {
|
||||
return uninit_null();
|
||||
@@ -674,8 +677,9 @@ bool Instance::t___isset(Variant v_name) {
|
||||
const Func* method = m_cls->lookupMethod(s___isset.get());
|
||||
if (method) {
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)&v, method,
|
||||
CREATE_VECTOR1(v_name), this);
|
||||
TypedValue args[1];
|
||||
tvDup(v_name.asTypedValue(), args + 0);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
|
||||
return v;
|
||||
} else {
|
||||
return uninit_null();
|
||||
@@ -686,8 +690,9 @@ Variant Instance::t___unset(Variant v_name) {
|
||||
const Func* method = m_cls->lookupMethod(s___unset.get());
|
||||
if (method) {
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)&v, method,
|
||||
CREATE_VECTOR1(v_name), this);
|
||||
TypedValue args[1];
|
||||
tvDup(v_name.asTypedValue(), args + 0);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
|
||||
return v;
|
||||
} else {
|
||||
return uninit_null();
|
||||
@@ -699,7 +704,7 @@ Variant Instance::t___sleep() {
|
||||
const Func *method = m_cls->lookupMethod(sd__sleep);
|
||||
if (method) {
|
||||
TypedValue tv;
|
||||
g_vmContext->invokeFunc(&tv, method, null_array, this);
|
||||
g_vmContext->invokeFuncFew(&tv, method, this);
|
||||
return tvAsVariant(&tv);
|
||||
} else {
|
||||
clearAttribute(HasSleep);
|
||||
@@ -712,7 +717,7 @@ Variant Instance::t___wakeup() {
|
||||
const Func *method = m_cls->lookupMethod(sd__wakeup);
|
||||
if (method) {
|
||||
TypedValue tv;
|
||||
g_vmContext->invokeFunc(&tv, method, null_array, this);
|
||||
g_vmContext->invokeFuncFew(&tv, method, this);
|
||||
return tvAsVariant(&tv);
|
||||
} else {
|
||||
return uninit_null();
|
||||
@@ -724,8 +729,9 @@ Variant Instance::t___set_state(Variant v_properties) {
|
||||
const Func* method = m_cls->lookupMethod(sd__set_state);
|
||||
if (method) {
|
||||
Variant v;
|
||||
g_vmContext->invokeFunc((TypedValue*)&v, method,
|
||||
CREATE_VECTOR1(v_properties), this);
|
||||
TypedValue args[1];
|
||||
tvDup(v_properties.asTypedValue(), args + 0);
|
||||
g_vmContext->invokeFuncFew((TypedValue*)&v, method, this, nullptr, 1, args);
|
||||
return v;
|
||||
} else {
|
||||
return false;
|
||||
@@ -736,7 +742,7 @@ String Instance::t___tostring() {
|
||||
const Func *method = m_cls->getToString();
|
||||
if (method) {
|
||||
TypedValue tv;
|
||||
g_vmContext->invokeFunc(&tv, method, null_array, this);
|
||||
g_vmContext->invokeFuncFew(&tv, method, this);
|
||||
if (!IS_STRING_TYPE(tv.m_type)) {
|
||||
void (*notify_user)(const char *, ...) = &raise_error;
|
||||
if (hphpiCompat) {
|
||||
@@ -759,7 +765,7 @@ Variant Instance::t___clone() {
|
||||
const Func *method = m_cls->lookupMethod(sd__clone);
|
||||
if (method) {
|
||||
TypedValue tv;
|
||||
g_vmContext->invokeFunc(&tv, method, null_array, this);
|
||||
g_vmContext->invokeFuncFew(&tv, method, this);
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@@ -1127,8 +1127,8 @@ static bool shouldIRInline(const Func* curFunc,
|
||||
if (func->numIterators() != 0) {
|
||||
return refuse("iterators");
|
||||
}
|
||||
if (func->maxStackCells() >= kStackCheckPadding) {
|
||||
FTRACE(1, "{} >= {}\n", func->maxStackCells(), kStackCheckPadding);
|
||||
if (func->maxStackCells() >= kStackCheckLeafPadding) {
|
||||
FTRACE(1, "{} >= {}\n", func->maxStackCells(), kStackCheckLeafPadding);
|
||||
return refuse("too many stack cells");
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ struct VMRegAnchor : private boost::noncopyable {
|
||||
m_old = REGSTATE_DIRTY;
|
||||
tl_regState = REGSTATE_CLEAN;
|
||||
|
||||
auto prevAr = (ActRec*)ar->m_savedRbp;
|
||||
auto prevAr = g_vmContext->getOuterVMFrame(ar);
|
||||
const Func* prevF = prevAr->m_func;
|
||||
vmsp() = ar->m_func->isGenerator() ?
|
||||
Stack::generatorStackBase(ar) :
|
||||
|
||||
@@ -1652,13 +1652,12 @@ TranslatorX64::shuffleArgsForMagicCall(ActRec* ar) {
|
||||
*/
|
||||
static void sync_regstate_to_caller(ActRec* preLive) {
|
||||
assert(tl_regState == REGSTATE_DIRTY);
|
||||
vmfp() = (TypedValue*)preLive->m_savedRbp;
|
||||
vmsp() = (TypedValue*)preLive - preLive->numArgs();
|
||||
if (ActRec* fp = g_vmContext->m_fp) {
|
||||
if (fp->m_func && fp->m_func->unit()) {
|
||||
vmpc() = fp->m_func->unit()->at(fp->m_func->base() + preLive->m_soff);
|
||||
}
|
||||
}
|
||||
VMExecutionContext* ec = g_vmContext;
|
||||
ec->m_stack.top() = (TypedValue*)preLive - preLive->numArgs();
|
||||
ActRec* fp = preLive == ec->m_firstAR ?
|
||||
ec->m_nestedVMs.back().m_savedState.fp : (ActRec*)preLive->m_savedRbp;
|
||||
ec->m_fp = fp;
|
||||
ec->m_pc = fp->m_func->unit()->at(fp->m_func->base() + preLive->m_soff);
|
||||
tl_regState = REGSTATE_CLEAN;
|
||||
}
|
||||
|
||||
@@ -2032,11 +2031,11 @@ TranslatorX64::funcPrologue(Func* func, int nPassed, ActRec* ar) {
|
||||
/*
|
||||
* Guard: we have stack enough stack space to complete this
|
||||
* function. We omit overflow checks if it is a leaf function
|
||||
* that can't use more than kStackCheckPadding cells.
|
||||
* that can't use more than kStackCheckLeafPadding cells.
|
||||
*/
|
||||
auto const needStackCheck =
|
||||
!(func->attrs() & AttrPhpLeafFn) ||
|
||||
func->maxStackCells() >= kStackCheckPadding;
|
||||
func->maxStackCells() >= kStackCheckLeafPadding;
|
||||
if (needStackCheck) {
|
||||
emitStackCheck(cellsToBytes(func->maxStackCells()), func->base());
|
||||
}
|
||||
@@ -2238,7 +2237,7 @@ TranslatorX64::emitPrologue(Func* func, int nPassed) {
|
||||
a.xorl (eax, eax);
|
||||
}
|
||||
for (k = numLocals; k < func->numLocals(); ++k) {
|
||||
locToRegDisp(Location(Location::Local, k), &base, &disp);
|
||||
locToRegDisp(Location(Location::Local, k), &base, &disp, func);
|
||||
emitStoreTVType(a, eax, base[disp + TVOFF(m_type)]);
|
||||
}
|
||||
}
|
||||
@@ -2944,7 +2943,7 @@ struct TReqInfo {
|
||||
|
||||
|
||||
void
|
||||
TranslatorX64::enterTC(SrcKey sk, TCA start) {
|
||||
TranslatorX64::enterTC(TCA start, void* data) {
|
||||
using namespace TargetCache;
|
||||
|
||||
if (debug) {
|
||||
@@ -2953,9 +2952,17 @@ TranslatorX64::enterTC(SrcKey sk, TCA start) {
|
||||
}
|
||||
DepthGuard d;
|
||||
TReqInfo info;
|
||||
info.requestNum = -1;
|
||||
info.saved_rStashedAr = 0;
|
||||
if (UNLIKELY(!start)) start = getTranslation(sk, true);
|
||||
SrcKey sk;
|
||||
|
||||
if (LIKELY(start != nullptr)) {
|
||||
info.requestNum = data ? REQ_BIND_CALL : -1;
|
||||
info.saved_rStashedAr = (uintptr_t)data;
|
||||
} else {
|
||||
info.requestNum = -1;
|
||||
info.saved_rStashedAr = 0;
|
||||
sk = *(SrcKey*)data;
|
||||
start = getTranslation(sk, true);
|
||||
}
|
||||
for (;;) {
|
||||
assert(sizeof(Cell) == 16);
|
||||
assert(((uintptr_t)vmsp() & (sizeof(Cell) - 1)) == 0);
|
||||
@@ -2975,13 +2982,16 @@ TranslatorX64::enterTC(SrcKey sk, TCA start) {
|
||||
start = getTranslation(sk, true);
|
||||
}
|
||||
assert(start == (TCA)HPHP::VM::Transl::funcBodyHelperThunk ||
|
||||
isValidCodeAddress(start));
|
||||
isValidCodeAddress(start) ||
|
||||
(start == (TCA)HPHP::VM::Transl::fcallHelperThunk &&
|
||||
info.saved_rStashedAr == (uintptr_t)data));
|
||||
assert(!s_writeLease.amOwner());
|
||||
curFunc()->validate();
|
||||
const Func* func = (vmfp() ? (ActRec*)vmfp() : (ActRec*)data)->m_func;
|
||||
func->validate();
|
||||
INC_TPC(enter_tc);
|
||||
|
||||
TRACE(1, "enterTC: %p fp%p(%s) sp%p enter {\n", start,
|
||||
vmfp(), ((ActRec*)vmfp())->m_func->name()->data(), vmsp());
|
||||
vmfp(), func->name()->data(), vmsp());
|
||||
tl_regState = REGSTATE_DIRTY;
|
||||
|
||||
// We have to force C++ to spill anything that might be in a callee-saved
|
||||
|
||||
@@ -1003,8 +1003,24 @@ public:
|
||||
* bytecode interpreter (see enterVMWork). It operates on behalf of
|
||||
* a given nested invocation of the intepreter (calling back into it
|
||||
* as necessary for blocks that need to be interpreted).
|
||||
*
|
||||
* If start is not null, data will be used to initialize rStashedAr,
|
||||
* to enable us to run a jitted prolog;
|
||||
* otherwise, data should be a pointer to the SrcKey to start
|
||||
* translating from.
|
||||
*
|
||||
* But don't call this directly, use one of the helpers below
|
||||
*/
|
||||
void enterTC(SrcKey sk, TCA start);
|
||||
void enterTC(TCA start, void* data);
|
||||
void enterTCAtSrcKey(SrcKey& sk) {
|
||||
enterTC(nullptr, &sk);
|
||||
}
|
||||
void enterTCAtProlog(ActRec *ar, TCA start) {
|
||||
enterTC(start, ar);
|
||||
}
|
||||
void enterTCAfterProlog(TCA start) {
|
||||
enterTC(start, nullptr);
|
||||
}
|
||||
|
||||
TranslatorX64();
|
||||
virtual ~TranslatorX64();
|
||||
|
||||
@@ -1 +1 @@
|
||||
HipHop Fatal error: Stack overflow in %s on line 21
|
||||
HipHop Fatal error: Stack overflow in %s on line 31
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário