Example #1
0
c_Continuation::~c_Continuation() {
  ActRec* ar = actRec();

  if (ar->hasVarEnv()) {
    ar->getVarEnv()->detach(ar);
  } else {
    frame_free_locals_inl(ar, ar->m_func->numLocals());
  }
}
Variant f_get_called_class() {
  EagerCallerFrame cf;
  ActRec* ar = cf();
  if (ar) {
    if (ar->hasThis()) return Variant(ar->getThis()->o_getClassName());
    if (ar->hasClass()) return Variant(ar->getClass()->preClass()->name());
  }
  return Variant(false);
}
Example #3
0
Variant f_func_get_args() {
  EagerCallerFrame cf;
  ActRec* ar = cf.actRecForArgs();
  if (ar && ar->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) {
    raise_warning(
      "func_get_args():  Called from the global scope - no function context"
    );
    return false;
  }
  return hhvm_get_frame_args(ar);
}
Example #4
0
const Func*
StaticMethodCache::lookup(RDS::Handle handle, const NamedEntity *ne,
                          const StringData* clsName,
                          const StringData* methName) {
  StaticMethodCache* thiz = static_cast<StaticMethodCache*>
    (handleToPtr(handle));
  Stats::inc(Stats::TgtCache_StaticMethodMiss);
  Stats::inc(Stats::TgtCache_StaticMethodHit, -1);
  TRACE(1, "miss %s :: %s caller %p\n",
        clsName->data(), methName->data(), __builtin_return_address(0));
  Transl::VMRegAnchor _; // needed for lookupClsMethod.

  ActRec* ar = reinterpret_cast<ActRec*>(vmsp() - kNumActRecCells);
  const Func* f;
  VMExecutionContext* ec = g_vmContext;
  const Class* cls = Unit::loadClass(ne, clsName);
  if (UNLIKELY(!cls)) {
    raise_error(Strings::UNKNOWN_CLASS, clsName->data());
  }
  LookupResult res = ec->lookupClsMethod(f, cls, methName,
                                         nullptr, // there may be an active this,
                                               // but we can just fall through
                                               // in that case.
                                         arGetContextClass(ec->getFP()),
                                         false /*raise*/);
  if (LIKELY(res == LookupResult::MethodFoundNoThis &&
             !f->isAbstract() &&
             f->isStatic())) {
    f->validate();
    TRACE(1, "fill %s :: %s -> %p\n", clsName->data(),
          methName->data(), f);
    // Do the | here instead of on every call.
    thiz->m_cls = (Class*)(uintptr_t(cls) | 1);
    thiz->m_func = f;
    ar->setClass(const_cast<Class*>(cls));
    return f;
  }
  assert(res != LookupResult::MethodFoundWithThis); // Not possible: no this.
  // We've already sync'ed regs; this is some hard case, we might as well
  // just let the interpreter handle this entirely.
  assert(toOp(*vmpc()) == OpFPushClsMethodD);
  Stats::inc(Stats::Instr_InterpOneFPushClsMethodD);
  Stats::inc(Stats::Instr_TC, -1);
  ec->opFPushClsMethodD();
  // Return whatever func the instruction produced; if nothing was
  // possible we'll either have fataled or thrown.
  assert(ar->m_func);
  ar->m_func->validate();
  // Don't update the cache; this case was too scary to memoize.
  TRACE(1, "unfillable miss %s :: %s -> %p\n", clsName->data(),
        methName->data(), ar->m_func);
  // Indicate to the caller that there is no work to do.
  return nullptr;
}
Example #5
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());
  }
}
Example #6
0
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));
  } else {
    if (!fp->hasVarEnv()) {
      fp->setVarEnv(VarEnv::createLocal(fp));
    }
    fp->getVarEnv()->setWithRef(name, src);
  }
}
Example #7
0
void phpDebuggerStepIn() {
    // If this is called in the middle of a flow command we short-circuit the
    // other commands
    auto& req_data = RID();
    req_data.setDebuggerStepIn(true);
    req_data.setDebuggerStepOut(StepOutState::NONE);
    req_data.setDebuggerNext(false);

    // Ensure the flow filter is fresh
    auto flow_filter = getFlowFilter();
    flow_filter->clear();

    // Check if the site is valid.
    VMRegAnchor _;
    ActRec* fp = vmfp();
    PC pc = vmpc();
    if (fp == nullptr || pc == nullptr) {
        TRACE(5, "Could not grab stack or program counter\n");
        return;
    }

    // Try to get needed context info. Bail if we can't
    const Func* func = fp->func();
    const Unit* unit = func != nullptr ? func->unit() : nullptr;
    if (func == nullptr || func == nullptr) {
        TRACE(5, "Could not grab the current unit or function\n");
        return;
    }

    // We use line1 here because it works better than line0 in our
    // bytecode-source mapping.
    int line;
    SourceLoc source_loc;
    if (unit->getSourceLoc(unit->offsetOf(pc), source_loc)) {
        line = source_loc.line1;
    } else {
        TRACE(5, "Could not grab the current line number\n");
        return;
    }

    TRACE(3, "Prepare location filter for %s:%d, unit %p:\n",
          unit->filepath()->data(), line, unit);

    // Get offset ranges for the whole line.
    OffsetRangeVec ranges;
    if (!unit->getOffsetRanges(line, ranges)) {
        ranges.clear();
    }

    flow_filter->addRanges(unit, ranges);
}
Example #8
0
int64 f_func_num_args() {
  if (hhvm) {
    CallerFrame cf;
    ActRec* ar = cf();
    if (ar == NULL) {
      return -1;
    }
    return ar->numArgs();
  } else {
    // we shouldn't be here, since code generation will inline this function
    ASSERT(false);
    return -1;
  }
}
Example #9
0
int64_t f_func_num_args() {
  EagerCallerFrame cf;
  ActRec* ar = cf.actRecForArgs();
  if (ar == NULL) {
    return -1;
  }
  if (ar->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) {
    raise_warning(
      "func_num_args():  Called from the global scope - no function context"
    );
    return -1;
  }
  return ar->numArgs();
}
Example #10
0
int DebuggerProxy::getStackDepth() {
  TRACE(2, "DebuggerProxy::getStackDepth\n");
  int depth = 0;
  VMExecutionContext* context = g_vmContext;
  ActRec *fp = context->getFP();
  if (!fp) return 0;
  ActRec *prev = fp->arGetSfp();
  while (fp != prev) {
    fp = prev;
    prev = fp->arGetSfp();
    depth++;
  }
  return depth;
}
Example #11
0
c_Generator::~c_Generator() {
  if (LIKELY(getState() == State::Done)) {
    return;
  }

  assert(getState() != State::Running);
  tvRefcountedDecRef(m_key);
  tvRefcountedDecRef(m_value);

  // Free locals, but don't trigger the EventHook for FunctionReturn since
  // the generator has already been exited. We don't want redundant calls.
  ActRec* ar = actRec();
  frame_free_locals_inl_no_hook<false>(ar, ar->func()->numLocals());
}
Example #12
0
int64_t HHVM_FUNCTION(func_num_args) {
  EagerCallerFrame cf;
  ActRec* ar = cf.actRecForArgs();
  if (ar == nullptr) {
    return -1;
  }
  if (ar->func()->isPseudoMain()) {
    raise_warning(
      "func_num_args():  Called from the global scope - no function context"
    );
    return -1;
  }
  return ar->numArgs();
}
Example #13
0
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));
  } 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);
  }
}
Example #14
0
ALWAYS_INLINE
static int64_t func_num_args_impl() {
  EagerCallerFrame cf;
  ActRec* ar = cf.actRecForArgs();
  if (ar == nullptr) {
    return -1;
  }
  if (ar->func()->isPseudoMain()) {
    raise_warning(
      "func_num_args():  Called from the global scope - no function context"
    );
    return -1;
  }
  return ar->numArgs();
}
Example #15
0
Variant f_get_called_class() {
  CallerFrame cf;
  ActRec* ar = cf();
  if (ar == NULL) {
    return Variant(false);
  }
  if (ar->hasThis()) {
    ObjectData* obj = ar->getThis();
    return obj->o_getClassName();
  } else if (ar->hasClass()) {
    return ar->getClass()->preClass()->name()->data();
  } else {
    return Variant(false);
  }
}
Example #16
0
ALWAYS_INLINE
static Variant func_get_arg_impl(int arg_num) {
  CallerFrame cf;
  ActRec* ar = cf.actRecForArgs();

  if (ar == nullptr) {
    return false;
  }
  if (ar->func()->isPseudoMain()) {
    raise_warning(
      "func_get_arg():  Called from the global scope - no function context"
    );
    return false;
  }
  if (arg_num < 0) {
    raise_warning(
      "func_get_arg():  The argument number should be >= 0"
    );
    return false;
  }
  if (arg_num >= ar->numArgs()) {
    raise_warning(
      "func_get_arg():  Argument %d not passed to function", arg_num
    );
    return false;
  }

  const int numParams = ar->m_func->numNonVariadicParams();

  if (arg_num < numParams) {
    // Formal parameter. Value is on the stack.
    TypedValue* loc =
      (TypedValue*)(uintptr_t(ar) - (arg_num + 1) * sizeof(TypedValue));
    return tvAsVariant(loc);
  }

  const int numArgs = ar->numArgs();
  const int extraArgs = numArgs - numParams;

  // Not a formal parameter.  Value is potentially in the
  // ExtraArgs/VarEnv.
  const int extraArgNum = arg_num - numParams;
  if (extraArgNum < extraArgs) {
    return tvAsVariant(ar->getExtraArg(extraArgNum));
  }

  return false;
}
Variant HHVM_FUNCTION(get_called_class) {
  EagerCallerFrame cf;
  ActRec* ar = cf();
  if (ar) {
    if (ar->hasThis()) {
      return Variant(ar->getThis()->getClassName());
    }
    if (ar->hasClass()) {
      return Variant(ar->getClass()->preClass()->name(),
        Variant::StaticStrInit{});
    }
  }

  raise_warning("get_called_class() called from outside a class");
  return Variant(false);
}
Example #18
0
c_Continuation::~c_Continuation() {
  ActRec* ar = actRec();

  // The first local is the object itself, and it wasn't increffed at creation
  // time (see createContinuation()). Overwrite its type to exempt it from
  // refcounting here.
  TypedValue* contLocal = frame_local(ar, 0);
  assert(contLocal->m_data.pobj == this);
  contLocal->m_type = KindOfNull;

  if (ar->hasVarEnv()) {
    ar->getVarEnv()->detach(ar);
  } else {
    frame_free_locals_inl(ar, m_vmFunc->numLocals());
  }
}
Example #19
0
File* GlobStreamWrapper::open(const String& filename,
                              const String& mode,
                              int options,
                              CVarRef context) {
  // Can't open a glob as a file, it's meant to be opened as a directory

  // if the function was called via FCallBuiltin, we'll get a bogus name as
  // the stack frame will be wrong
  ActRec* ar = g_vmContext->getStackFrame();
  const char* fn = (ar != nullptr)
    ? ar->func()->name()->data()
    : "OPTIMIZED_BUILTIN";
  raise_warning("%s(%s): failed to open stream: "
                "wrapper does not support stream open",
                fn, filename.data());
  return nullptr;
}
c_Continuation *c_Continuation::clone() {
  const Func *origFunc = m_origFunc;
  const Func *genFunc = 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(actRec());

  cont->o_subclassData.u16 = o_subclassData.u16;
  cont->m_label = m_label;
  cont->m_index = m_index;
  cont->m_key   = m_key;
  cont->m_value = m_value;

  return cont;
}
Example #21
0
const Func*
StaticMethodCache::lookupIR(RDS::Handle handle, const NamedEntity *ne,
                            const StringData* clsName,
                            const StringData* methName, TypedValue* vmfp,
                            TypedValue* vmsp) {
  StaticMethodCache* thiz = static_cast<StaticMethodCache*>
    (handleToPtr(handle));
  Stats::inc(Stats::TgtCache_StaticMethodMiss);
  Stats::inc(Stats::TgtCache_StaticMethodHit, -1);
  TRACE(1, "miss %s :: %s caller %p\n",
        clsName->data(), methName->data(), __builtin_return_address(0));

  ActRec* ar = reinterpret_cast<ActRec*>(vmsp - kNumActRecCells);
  const Func* f;
  VMExecutionContext* ec = g_vmContext;
  const Class* cls = Unit::loadClass(ne, clsName);
  if (UNLIKELY(!cls)) {
    raise_error(Strings::UNKNOWN_CLASS, clsName->data());
  }
  LookupResult res = ec->lookupClsMethod(f, cls, methName,
                                         nullptr, // there may be an active this,
                                               // but we can just fall through
                                               // in that case.
                                         arGetContextClass((ActRec*)vmfp),
                                         false /*raise*/);
  if (LIKELY(res == LookupResult::MethodFoundNoThis &&
             !f->isAbstract() &&
             f->isStatic())) {
    f->validate();
    TRACE(1, "fill %s :: %s -> %p\n", clsName->data(),
          methName->data(), f);
    // Do the | here instead of on every call.
    thiz->m_cls = (Class*)(uintptr_t(cls) | 1);
    thiz->m_func = f;
    ar->setClass(const_cast<Class*>(cls));
    return f;
  }
  assert(res != LookupResult::MethodFoundWithThis); // Not possible: no this.

  // Indicate to the IR that it should take even slower path
  return nullptr;
}
Example #22
0
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;
}
Example #23
0
// stack trace helper
static ProfileStackTrace getStackTrace() {
  ProfileStackTrace trace;

  if (g_context.isNull()) return trace;
  VMRegAnchor _;
  ActRec *fp = vmfp();
  if (!fp) return trace;
  PC pc = vmpc();

  const Func *f = fp->m_func;
  Unit *u = f->unit();
  Offset off = pc - u->entry();
  for (;;) {
    trace.push_back({ f, off, fp->resumed() });
    fp = g_context->getPrevVMStateUNSAFE(fp, &off);
    if (!fp) break;
    f = fp->m_func;
  }
  return trace;
}
Example #24
0
// stack trace helper
static ProfileStackTrace getStackTrace() {
    ProfileStackTrace trace;

    if (g_context.isNull()) return trace;
    JIT::VMRegAnchor _;
    ActRec *fp = g_context->getFP();
    if (!fp) return trace;
    PC pc = g_context->getPC();

    const Func *f = fp->m_func;
    Unit *u = f->unit();
    Offset off = pc - u->entry();
    for (;;) {
        trace.push_back({ f, off, fp->inGenerator() });
        fp = g_context->getPrevVMState(fp, &off);
        if (!fp) break;
        f = fp->m_func;
    }
    return trace;
}
void TranslatorX64::fCallArrayHelper(const Offset pcOff, const Offset pcNext) {
    DECLARE_FRAME_POINTER(framePtr);
    ActRec* fp = (ActRec*)framePtr->m_savedRbp;

    VMExecutionContext *ec = g_vmContext;
    ec->m_fp = fp;
    ec->m_stack.top() = sp;
    ec->m_pc = fp->unit()->at(pcOff);
    PC pc = fp->unit()->at(pcNext);

    tl_regState = VMRegState::CLEAN;
    bool runFunc = ec->doFCallArray(pc);
    sp = ec->m_stack.top();
    tl_regState = VMRegState::DIRTY;
    if (!runFunc) return;

    ec->m_fp->m_savedRip = framePtr->m_savedRip;
    // smash our return and frame pointer chain
    framePtr->m_savedRip = (uint64_t)ec->m_fp->m_func->getFuncBody();
    framePtr->m_savedRbp = (uint64_t)ec->m_fp;
}
Example #26
0
Variant f_get_called_class() {
  if (hhvm) {
    CallerFrame cf;
    ActRec* ar = cf();
    if (ar == NULL) {
      return Variant(false);
    }
    if (ar->hasThis()) {
      ObjectData* obj = ar->getThis();
      return obj->o_getClassName();
    } else if (ar->hasClass()) {
      return ar->getClass()->preClass()->name()->data();
    } else {
      return Variant(false);
    }
  } else {
    CStrRef cls = FrameInjection::GetStaticClassName(
      ThreadInfo::s_threadInfo.getNoCheck());
    return cls.size() ? Variant(cls.get()) : Variant(false);
  }
}
Example #27
0
void Injection::execute() const {
  if (m_builtin) {
    ASSERT(m_callback);
    // Execute function in runtime
    m_callback(m_arg);
    return;
  }
  // Execute php code piece
  TypedValue retval;
  VarEnv *varEnv = NULL;
  ActRec *cfpSave = NULL;
  ObjectData *this_ = NULL;
  Class *cls = NULL;
  ActRec *fp = g_vmContext->getFP();
  if (fp) {
    if (!fp->hasVarEnv()) {
      fp->m_varEnv = VarEnv::createLazyAttach(fp);
    }
    varEnv = fp->m_varEnv;
    cfpSave = varEnv->getCfp();
    if (fp->hasThis()) {
      this_ = fp->getThis();
    } else if (fp->hasClass()) {
      cls = fp->getClass();
    }
  }
  // Note: For now we don't merge analysis code's class and function.
  // Later we might decide to do so
  g_vmContext->invokeFunc(&retval, m_unit->getMain(), Array::Create(), this_,
                          cls, varEnv, NULL, NULL);
  if (varEnv) {
    varEnv->setCfp(cfpSave);
  }
}
Example #28
0
void XDebugProfiler::enableTracing(const String& filename, int64_t opts) {
  assert(!m_tracingEnabled);

  // Attempt to open the passed filename. php5 xdebug doesn't enable tracing
  // if we cannot open the file, so we need to open it now as opposed to when we
  // actually do the writing in order to ensure we handle this case. We keep the
  // file handle open in order to ensure we can still write on tracing stop
  FILE* file;
  if (opts & k_XDEBUG_TRACE_APPEND) {
    file = fopen(filename.data(), "a");
  } else {
    file = fopen(filename.data(), "w");
  }

  // If file is null, opening the passed filename failed. php5 xdebug doesn't
  // do anything in this case, but we should probably notify the user
  if (file == nullptr) {
    raise_warning("xdebug profiler failed to open tracing file %s for writing.",
                  filename.data());
    return;
  }

  m_tracingEnabled = true;
  m_tracingStartIdx = m_nextFrameIdx;
  m_tracingFilename = filename;
  m_tracingFile = file;
  m_tracingOpts = opts;

  // If we're not at the top level, need to grab the call sites for each frame
  // on the stack.
  VMRegAnchor _;
  Offset offset;
  ActRec* fp = vmfp();
  while ((fp = g_context->getPrevVMState(fp, &offset)) != nullptr) {
    FrameData frame;
    frame.func = fp->func();
    frame.line = fp->unit()->getLineNumber(offset);
    m_tracingStartFrameData.push_back(frame);
  }
}
Example #29
0
zval* ZendExecutionStack::getArg(int i) {
  auto& stack = getStack();
  auto& entry = stack.m_stack.back();
  switch (entry.mode) {
    case ZendStackMode::HHVM_STACK: {
      ActRec* ar = (ActRec*)entry.value;
      const int numNonVaradic = ar->m_func->numNonVariadicParams();
      TypedValue* arg;
      if (i < numNonVaradic) {
        arg = (TypedValue*)ar - i - 1;
      } else if (i < ar->numArgs()) {
        arg = ar->getExtraArg(i - numNonVaradic);
      } else {
        if (!stack.m_nullArg) {
          stack.m_nullArg = RefData::Make(make_tv<KindOfNull>());
        }
        return stack.m_nullArg;
      }

      zBoxAndProxy(arg);
      return arg->m_data.pref;
    }

    case ZendStackMode::SIDE_STACK: {
      // Zend puts the number of args as the last thing on the stack
      int numargs = uintptr_t(entry.value);
      assert(numargs < 4096);
      assert(i < numargs);
      zval* zv =
        (zval*) stack.m_stack[stack.m_stack.size() - 1 - numargs + i].value;
      zv->assertValid();
      return zv;
    }
  }
  not_reached();
  return nullptr;
}
Example #30
0
bool EventHook::RunInterceptHandler(ActRec* ar) {
  const Func* func = ar->func();
  if (LIKELY(func->maybeIntercepted() == 0)) return true;

  // Intercept only original generator / async function calls, not resumption.
  if (ar->resumed()) return true;

  Variant* h = get_intercept_handler(func->fullNameStr(),
                                     &func->maybeIntercepted());
  if (!h) return true;

  /*
   * In production mode, only functions that we have assumed can be
   * intercepted during static analysis should actually be
   * intercepted.
   */
  if (RuntimeOption::RepoAuthoritative &&
      !RuntimeOption::EvalJitEnableRenameFunction) {
    if (!(func->attrs() & AttrInterceptable)) {
      raise_error("fb_intercept was used on a non-interceptable function (%s) "
                  "in RepoAuthoritative mode", func->fullName()->data());
    }
  }

  VMRegAnchor _;

  PC savePc = vmpc();

  Variant doneFlag = true;
  Variant called_on;

  if (ar->hasThis()) {
    called_on = Variant(ar->getThis());
  } else if (ar->hasClass()) {
    // For static methods, give handler the name of called class
    called_on = Variant(const_cast<StringData*>(ar->getClass()->name()));
  }
  Variant intArgs =
    PackedArrayInit(5)
      .append(VarNR(ar->func()->fullName()))
      .append(called_on)
      .append(get_frame_args_with_ref(ar))
      .append(h->asCArrRef()[1])
      .appendRef(doneFlag)
      .toArray();

  Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs);
  if (doneFlag.toBoolean()) {
    Offset pcOff;
    ActRec* outer = g_context->getPrevVMState(ar, &pcOff);

    frame_free_locals_inl_no_hook<true>(ar, ar->func()->numLocals());
    Stack& stack = vmStack();
    stack.top() = (Cell*)(ar + 1);
    cellDup(*ret.asCell(), *stack.allocTV());

    vmfp() = outer;
    vmpc() = outer ? outer->func()->unit()->at(pcOff) : nullptr;

    return false;
  }
  vmfp() = ar;
  vmpc() = savePc;

  return true;
}