88cd96db87
The C++ vtable pointers and the m_cls pointers in ObjectData are
redundant for C++ extension classes; they both indicate the ObjectData's
dynamic type.
This diff gives ObjectData the same treatment that the ArrayData
hierarchy got, replacing virtual functions with manual dispatch based on
attributes and m_cls. We can't do direct comparison against m_cls,
because of inheritance, except when we know inheritance is not in the
picture.
The vtable isn't gone yet, because I need to do something about C++
destructors, which still require virtual dispatch. That's going to
require a lot of grunt work, so I wanted to put this diff up to keep
that stuff separate.
I did, however, make ~ObjectData non-virtual and try to compile, to
flush out all the dynamic_casts of ObjectData.
- The type-casting functions and clone() are pretty straightforward. The
only trick is that specialized clone() functions now have to call
cloneImpl() instead of the base class' clone(), otherwise infinite
recursion. I added an ObjectData attribute to indicate whether clone()
is special.
- t___{sleep,wakeup} didn't even need to be virtual, it turns out.
ObjectData's implementation goes through the normal PHP-method lookup
path, which effectively does virtual dispatch and routes us to any
specialized implementations anyway.
Differential Revision: D940852
274 linhas
8.4 KiB
C++
Arquivo Executável
274 linhas
8.4 KiB
C++
Arquivo Executável
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "hphp/runtime/ext/ext_continuation.h"
|
|
#include "hphp/runtime/ext/ext_asio.h"
|
|
#include "hphp/runtime/base/builtin-functions.h"
|
|
|
|
#include "hphp/runtime/ext/ext_spl.h"
|
|
#include "hphp/runtime/ext/ext_variable.h"
|
|
#include "hphp/runtime/ext/ext_function.h"
|
|
|
|
#include "hphp/runtime/vm/jit/translator.h"
|
|
#include "hphp/runtime/vm/jit/translator-inline.h"
|
|
#include "hphp/runtime/vm/func.h"
|
|
#include "hphp/runtime/vm/runtime.h"
|
|
#include "hphp/runtime/base/stats.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
Object f_hphp_create_continuation(CStrRef clsname,
|
|
CStrRef funcname,
|
|
CStrRef origFuncName,
|
|
CArrRef args /* = null_array */) {
|
|
throw_fatal("Invalid call hphp_create_continuation");
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
c_Continuation::c_Continuation(Class* cb)
|
|
: ExtObjectDataFlags<ObjectData::HasClone>(cb)
|
|
, m_label(0)
|
|
, m_index(-1LL)
|
|
, m_key(-1LL)
|
|
, m_value(Variant::NullInit())
|
|
, m_origFunc(nullptr) {
|
|
o_subclassData.u16 = 0;
|
|
}
|
|
|
|
c_Continuation::~c_Continuation() {
|
|
ActRec* ar = actRec();
|
|
|
|
if (ar->hasVarEnv()) {
|
|
ar->getVarEnv()->detach(ar);
|
|
} else {
|
|
// Free locals, but don't trigger the EventHook for FunctionExit
|
|
// since the continuation function has already been exited. We
|
|
// don't want redundant calls.
|
|
frame_free_locals_inl_no_hook<false>(ar, ar->m_func->numLocals());
|
|
}
|
|
}
|
|
|
|
void c_Continuation::t___construct() {}
|
|
|
|
void c_Continuation::t_update(int64_t label, CVarRef value) {
|
|
m_label = label;
|
|
assert(m_label == label); // check m_label for truncation
|
|
m_value.assignVal(value);
|
|
m_key = ++m_index;
|
|
}
|
|
|
|
void c_Continuation::t_update_key(int64_t label, CVarRef key, CVarRef value) {
|
|
m_label = label;
|
|
assert(m_label == label); // check m_label for truncation
|
|
m_key.assignVal(key);
|
|
m_value.assignVal(value);
|
|
if (m_key.isInteger()) {
|
|
int64_t new_index = m_key.toInt64Val();
|
|
m_index = new_index > m_index ? new_index : m_index;
|
|
}
|
|
}
|
|
|
|
Object c_Continuation::t_getwaithandle() {
|
|
if (m_waitHandle.isNull()) {
|
|
c_ContinuationWaitHandle::Create(this);
|
|
assert(!m_waitHandle.isNull());
|
|
}
|
|
return m_waitHandle;
|
|
}
|
|
|
|
int64_t c_Continuation::t_getlabel() {
|
|
return m_label;
|
|
}
|
|
|
|
Variant c_Continuation::t_current() {
|
|
const_assert(false);
|
|
return m_value;
|
|
}
|
|
|
|
Variant c_Continuation::t_key() {
|
|
startedCheck();
|
|
return m_key;
|
|
}
|
|
|
|
void c_Continuation::t_next() {
|
|
const_assert(false);
|
|
}
|
|
|
|
const StaticString
|
|
s_next("next"),
|
|
s__closure_("{closure}"),
|
|
s_this("this");
|
|
|
|
void c_Continuation::t_rewind() {
|
|
this->o_invoke_few_args(s_next, 0);
|
|
}
|
|
|
|
bool c_Continuation::t_valid() {
|
|
const_assert(false);
|
|
return !done();
|
|
}
|
|
|
|
void c_Continuation::t_send(CVarRef v) {
|
|
const_assert(false);
|
|
}
|
|
|
|
void c_Continuation::t_raise(CVarRef v) {
|
|
const_assert(false);
|
|
}
|
|
|
|
String c_Continuation::t_getorigfuncname() {
|
|
auto const origName = m_origFunc->isClosureBody() ? s__closure_.get()
|
|
: m_origFunc->name();
|
|
assert(origName->isStatic());
|
|
return String(const_cast<StringData*>(origName));
|
|
}
|
|
|
|
String c_Continuation::t_getcalledclass() {
|
|
String called_class;
|
|
|
|
if (actRec()->hasThis()) {
|
|
called_class = actRec()->getThis()->getVMClass()->name()->data();
|
|
} else if (actRec()->hasClass()) {
|
|
called_class = actRec()->getClass()->name()->data();
|
|
} else {
|
|
called_class = empty_string;
|
|
}
|
|
|
|
return called_class;
|
|
}
|
|
|
|
void c_Continuation::dupContVar(const StringData* name, TypedValue* src) {
|
|
ActRec *fp = actRec();
|
|
Id destId = fp->m_func->lookupVarId(name);
|
|
if (destId != kInvalidId) {
|
|
// Copy the value of the local to the cont object.
|
|
tvDupFlattenVars(src, frame_local(fp, destId), nullptr);
|
|
} else {
|
|
if (!fp->hasVarEnv()) {
|
|
// This VarEnv may potentially outlive the most recently stack-allocated
|
|
// VarEnv, so we need to heap allocate it.
|
|
fp->setVarEnv(VarEnv::createLocalOnHeap(fp));
|
|
}
|
|
fp->getVarEnv()->setWithRef(name, src);
|
|
}
|
|
}
|
|
|
|
void c_Continuation::copyContinuationVars(ActRec* fp) {
|
|
// For functions that contain only named locals, we can copy TVs
|
|
// right to the local space.
|
|
static const StringData* thisStr = s_this.get();
|
|
bool skipThis;
|
|
if (fp->hasVarEnv()) {
|
|
Stats::inc(Stats::Cont_CreateVerySlow);
|
|
Array definedVariables = fp->getVarEnv()->getDefinedVariables();
|
|
skipThis = definedVariables.exists(s_this, true);
|
|
|
|
for (ArrayIter iter(definedVariables); !iter.end(); iter.next()) {
|
|
dupContVar(iter.first().getStringData(),
|
|
const_cast<TypedValue *>(iter.secondRef().asTypedValue()));
|
|
}
|
|
} else {
|
|
const Func *genFunc = actRec()->m_func;
|
|
skipThis = genFunc->lookupVarId(thisStr) != kInvalidId;
|
|
for (Id i = 0; i < genFunc->numNamedLocals(); ++i) {
|
|
dupContVar(genFunc->localVarName(i), frame_local(fp, i));
|
|
}
|
|
}
|
|
|
|
// If $this is used as a local inside the body and is not provided
|
|
// by our containing environment, just prefill it here instead of
|
|
// using InitThisLoc inside the body
|
|
if (!skipThis && fp->hasThis()) {
|
|
Id id = actRec()->m_func->lookupVarId(thisStr);
|
|
if (id != kInvalidId) {
|
|
tvAsVariant(frame_local(actRec(), id)) = fp->getThis();
|
|
}
|
|
}
|
|
}
|
|
|
|
c_Continuation *c_Continuation::Clone(ObjectData* obj) {
|
|
auto thiz = static_cast<c_Continuation*>(obj);
|
|
const Func *origFunc = thiz->m_origFunc;
|
|
const Func *genFunc = thiz->actRec()->m_func;
|
|
|
|
ActRec *fp = g_vmContext->getFP();
|
|
c_Continuation* cont = origFunc->isMethod()
|
|
? g_vmContext->createContMeth(origFunc, genFunc, fp->getThisOrClass())
|
|
: g_vmContext->createContFunc(origFunc, genFunc);
|
|
|
|
cont->copyContinuationVars(thiz->actRec());
|
|
|
|
cont->o_subclassData.u16 = thiz->o_subclassData.u16;
|
|
cont->m_label = thiz->m_label;
|
|
cont->m_index = thiz->m_index;
|
|
cont->m_key = thiz->m_key;
|
|
cont->m_value = thiz->m_value;
|
|
|
|
return cont;
|
|
}
|
|
|
|
namespace {
|
|
StaticString s_send("send");
|
|
StaticString s_raise("raise");
|
|
}
|
|
|
|
void c_Continuation::call_next() {
|
|
const HPHP::Func* func = m_cls->lookupMethod(s_next.get());
|
|
g_vmContext->invokeContFunc(func, this);
|
|
}
|
|
|
|
void c_Continuation::call_send(Cell& v) {
|
|
const HPHP::Func* func = m_cls->lookupMethod(s_send.get());
|
|
g_vmContext->invokeContFunc(func, this, &v);
|
|
}
|
|
|
|
void c_Continuation::call_raise(ObjectData* e) {
|
|
assert(e);
|
|
assert(e->instanceof(SystemLib::s_ExceptionClass));
|
|
|
|
const HPHP::Func* func = m_cls->lookupMethod(s_raise.get());
|
|
|
|
Cell arg;
|
|
arg.m_type = KindOfObject;
|
|
arg.m_data.pobj = e;
|
|
|
|
g_vmContext->invokeContFunc(func, this, &arg);
|
|
}
|
|
|
|
// Compute the bytecode offset at which execution will resume when
|
|
// this continuation resumes. Only valid on started but not actually
|
|
// running continuations.
|
|
Offset c_Continuation::getNextExecutionOffset() const {
|
|
assert(started() && !running());
|
|
auto func = m_arPtr->m_func;
|
|
PC funcBase = func->unit()->entry() + func->base();
|
|
assert(toOp(*funcBase) == OpUnpackCont); // One byte
|
|
PC switchOffset = funcBase + 1;
|
|
assert(toOp(*switchOffset) == OpSwitch);
|
|
// The Switch opcode is one byte for the opcode itself, plus four
|
|
// bytes for the jmp table size, then the jump table.
|
|
Offset* jmpTable = (Offset*)(switchOffset + 5);
|
|
Offset relOff = jmpTable[m_label];
|
|
return func->base() + relOff + 1;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|