Example #1
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 #2
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());
  }
}
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
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 #5
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 #6
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 #7
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 #8
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->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) {
    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;
}
Example #9
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 #10
0
Array createBacktrace(const BacktraceArgs& btArgs) {
  auto bt = Array::Create();

  // If there is a parser frame, put it at the beginning of the backtrace.
  if (btArgs.m_parserFrame) {
    bt.append(
      make_map_array(
        s_file, btArgs.m_parserFrame->filename,
        s_line, btArgs.m_parserFrame->lineNumber
      )
    );
  }

  VMRegAnchor _;
  // If there are no VM frames, we're done.
  if (!rds::header() || !vmfp()) return bt;

  int depth = 0;
  ActRec* fp = nullptr;
  Offset pc = 0;

  // Get the fp and pc of the top frame (possibly skipping one frame).

  if (btArgs.m_skipTop) {
    fp = getPrevActRec(vmfp(), &pc);
    // We skipped over the only VM frame, we're done.
    if (!fp) return bt;
  } else {
    fp = vmfp();
    auto const unit = fp->func()->unit();
    assert(unit);
    pc = unit->offsetOf(vmpc());
  }

  // Handle the top frame.
  if (btArgs.m_withSelf) {
    // Builtins don't have a file and line number.
    if (!fp->func()->isBuiltin()) {
      auto const unit = fp->func()->unit();
      assert(unit);
      auto const filename = fp->func()->filename();

      ArrayInit frame(btArgs.m_parserFrame ? 4 : 2, ArrayInit::Map{});
      frame.set(s_file, Variant{const_cast<StringData*>(filename)});
      frame.set(s_line, unit->getLineNumber(pc));
      if (btArgs.m_parserFrame) {
        frame.set(s_function, s_include);
        frame.set(s_args, Array::Create(btArgs.m_parserFrame->filename));
      }
      bt.append(frame.toVariant());
      depth++;
    }
  }

  // Handle the subsequent VM frames.
  Offset prevPc = 0;
  for (auto prevFp = getPrevActRec(fp, &prevPc);
       fp != nullptr && (btArgs.m_limit == 0 || depth < btArgs.m_limit);
       fp = prevFp, pc = prevPc,
         prevFp = getPrevActRec(fp, &prevPc)) {
    // Do not capture frame for HPHP only functions.
    if (fp->func()->isNoInjection()) continue;

    ArrayInit frame(7, ArrayInit::Map{});

    auto const curUnit = fp->func()->unit();
    auto const curOp = *reinterpret_cast<const Op*>(curUnit->at(pc));
    auto const isReturning =
      curOp == Op::RetC || curOp == Op::RetV ||
      curOp == Op::CreateCont || curOp == Op::Await ||
      fp->localsDecRefd();

    // Builtins and generators don't have a file and line number
    if (prevFp && !prevFp->func()->isBuiltin()) {
      auto const prevUnit = prevFp->func()->unit();
      auto prevFile = prevUnit->filepath();
      if (prevFp->func()->originalFilename()) {
        prevFile = prevFp->func()->originalFilename();
      }
      assert(prevFile);
      frame.set(s_file, Variant{const_cast<StringData*>(prevFile)});

      // In the normal method case, the "saved pc" for line number printing is
      // pointing at the cell conversion (Unbox/Pop) instruction, not the call
      // itself. For multi-line calls, this instruction is associated with the
      // subsequent line which results in an off-by-n. We're subtracting one
      // in order to look up the line associated with the FCall/FCallArray
      // instruction. Exception handling and the other opcodes (ex. BoxR)
      // already do the right thing. The emitter associates object access with
      // the subsequent expression and this would be difficult to modify.
      auto const opAtPrevPc =
        *reinterpret_cast<const Op*>(prevUnit->at(prevPc));
      Offset pcAdjust = 0;
      if (opAtPrevPc == Op::PopR ||
          opAtPrevPc == Op::UnboxR ||
          opAtPrevPc == Op::UnboxRNop) {
        pcAdjust = 1;
      }
      frame.set(s_line,
                prevFp->func()->unit()->getLineNumber(prevPc - pcAdjust));
    }

    // Check for include.
    String funcname{const_cast<StringData*>(fp->func()->name())};
    if (fp->func()->isClosureBody()) {
      // Strip the file hash from the closure name.
      String fullName{const_cast<StringData*>(fp->func()->baseCls()->name())};
      funcname = fullName.substr(0, fullName.find(';'));
    }

    // Check for pseudomain.
    if (funcname.empty()) {
      if (!prevFp && !btArgs.m_withPseudoMain) continue;
      else if (!prevFp) funcname = s_main;
      else funcname = s_include;
    }

    frame.set(s_function, funcname);

    if (!funcname.same(s_include)) {
      // Closures have an m_this but they aren't in object context.
      auto ctx = arGetContextClass(fp);
      if (ctx != nullptr && !fp->func()->isClosureBody()) {
        frame.set(s_class, Variant{const_cast<StringData*>(ctx->name())});
        if (fp->hasThis() && !isReturning) {
          if (btArgs.m_withThis) {
            frame.set(s_object, Object(fp->getThis()));
          }
          frame.set(s_type, s_arrow);
        } else {
          frame.set(s_type, s_double_colon);
        }
      }
    }

    bool const mayUseVV = fp->func()->attrs() & AttrMayUseVV;

    auto const withNames = btArgs.m_withArgNames;
    auto const withValues = btArgs.m_withArgValues;
    if (!btArgs.m_withArgNames && !btArgs.m_withArgValues) {
      // do nothing
    } else if (funcname.same(s_include)) {
      if (depth != 0) {
        auto filepath = const_cast<StringData*>(curUnit->filepath());
        frame.set(s_args, make_packed_array(filepath));
      }
    } else if (!RuntimeOption::EnableArgsInBacktraces || isReturning) {
      // Provide an empty 'args' array to be consistent with hphpc.
      frame.set(s_args, empty_array());
    } else {
      auto args = Array::Create();
      auto const nparams = fp->func()->numNonVariadicParams();
      auto const nargs = fp->numArgs();
      auto const nformals = std::min<int>(nparams, nargs);

      if (UNLIKELY(mayUseVV) &&
          UNLIKELY(fp->hasVarEnv() && fp->getVarEnv()->getFP() != fp)) {
        // VarEnv is attached to eval or debugger frame, other than the current
        // frame. Access locals thru VarEnv.
        auto varEnv = fp->getVarEnv();
        auto func = fp->func();
        for (int i = 0; i < nformals; i++) {
          auto const argname = func->localVarName(i);
          auto const tv = varEnv->lookup(argname);

          Variant val;
          if (tv != nullptr) { // the variable hasn't been unset
            val = withValues ? tvAsVariant(tv) : "";
          }

          if (withNames) {
            args.set(String(const_cast<StringData*>(argname)), val);
          } else {
            args.append(val);
          }
        }
      } else {
        for (int i = 0; i < nformals; i++) {
          Variant val = withValues ? tvAsVariant(frame_local(fp, i)) : "";

          if (withNames) {
            auto const argname = fp->func()->localVarName(i);
            args.set(String(const_cast<StringData*>(argname)), val);
          } else {
            args.append(val);
          }
        }
      }

      // Builtin extra args are not stored in varenv.
      if (UNLIKELY(mayUseVV) && nargs > nparams && fp->hasExtraArgs()) {
        for (int i = nparams; i < nargs; i++) {
          auto arg = fp->getExtraArg(i - nparams);
          args.append(tvAsVariant(arg));
        }
      }
      frame.set(s_args, args);
    }

    if (btArgs.m_withMetadata && !isReturning) {
      if (UNLIKELY(mayUseVV) && UNLIKELY(fp->hasVarEnv())) {
        auto tv = fp->getVarEnv()->lookup(s_86metadata.get());
        if (tv != nullptr && tv->m_type != KindOfUninit) {
          frame.set(s_metadata, tvAsVariant(tv));
        }
      } else {
        auto local = fp->func()->lookupVarId(s_86metadata.get());
        if (local != kInvalidId) {
          auto tv = frame_local(fp, local);
          if (tv->m_type != KindOfUninit) {
            frame.set(s_metadata, tvAsVariant(tv));
          }
        }
      }
    }

    bt.append(frame.toVariant());
    depth++;
  }

  return bt;
}
Example #11
0
bool UrlFile::open(const String& input_url, const String& mode) {
  String url = input_url;
  const char* modestr = mode.c_str();
  if (strchr(modestr, '+') || strchr(modestr, 'a') || strchr(modestr, 'w')) {
    std::string msg = "cannot open a url stream for write/append operation: ";
    msg += url.c_str();
    m_error = msg;
    return false;
  }
  HttpClient http(m_timeout, m_maxRedirect);
  auto ctx = this->getStreamContext();
  if (ctx) {
    http.setStreamContextOptions(ctx->getOptions());
  }
  m_response.clear();

  if (!m_proxyHost.empty()) {
    http.proxy(m_proxyHost, m_proxyPort, m_proxyUsername, m_proxyPassword);
  }

  HeaderMap *pHeaders = nullptr;
  HeaderMap requestHeaders;
  if (!m_headers.empty()) {
    pHeaders = &requestHeaders;
    for (ArrayIter iter(m_headers); iter; ++iter) {
      requestHeaders[std::string(iter.first().toString().data())].
        push_back(iter.second().toString().data());
    }
  }

  Variant user = f_parse_url(url, k_PHP_URL_USER);
  if (user.isString()) {
    Variant pass = f_parse_url(url, k_PHP_URL_PASS);
    http.auth(user.toString().c_str(), pass.toString().c_str());
    url = HHVM_FN(preg_replace)(
      s_remove_user_pass_pattern,
      s_remove_user_pass_replace,
      url,
      1
    ).toString();
  }

  int code;
  std::vector<String> responseHeaders;
  if (m_get) {
    code = http.get(url.c_str(), m_response, pHeaders, &responseHeaders);
  } else {
    code = http.request(m_method,
                        url.c_str(), m_postData.data(), m_postData.size(),
                        m_response, pHeaders, &responseHeaders);
  }

  m_responseHeaders.reset();
  for (unsigned int i = 0; i < responseHeaders.size(); i++) {
    m_responseHeaders.append(responseHeaders[i]);
  }
  VMRegAnchor vra;
  ActRec* fp = vmfp();
  while (fp->skipFrame()) {
    fp = g_context->getPrevVMState(fp);
  }
  auto id = fp->func()->lookupVarId(s_http_response_header.get());
  if (id != kInvalidId) {
    auto tvTo = frame_local(fp, id);
    Variant varFrom(m_responseHeaders);
    const auto tvFrom(varFrom.asTypedValue());
    if (tvTo->m_type == KindOfRef) {
      tvTo = tvTo->m_data.pref->tv();
    }
    tvDup(*tvFrom, *tvTo);
  } else if ((fp->func()->attrs() & AttrMayUseVV) && fp->hasVarEnv()) {
    fp->getVarEnv()->set(s_http_response_header.get(),
                         Variant(m_responseHeaders).asTypedValue());
  }

  /*
   * If code == 0, Curl failed to connect; per PHP5, ignore_errors just means
   * to not worry if we get an http resonse code that isn't between 200 and 400,
   * but we shouldn't ignore other errors.
   * all status codes in the 2xx range are defined by the specification as
   * successful;
   * all status codes in the 3xx range are for redirection, and so also should
   * never fail.
   */
  if ((code >= 200 && code < 400) || (m_ignoreErrors && code != 0)) {
    setName(url.toCppString());
    m_data = const_cast<char*>(m_response.data());
    m_len = m_response.size();
    return true;
  } else {
    m_error = http.getLastError().c_str();
    return false;
  }
}
Example #12
0
Array createBacktrace(const BacktraceArgs& btArgs) {
  Array bt = Array::Create();

  // If there is a parser frame, put it at the beginning of
  // the backtrace
  if (btArgs.m_parserFrame) {
    bt.append(
      make_map_array(
        s_file, btArgs.m_parserFrame->filename,
        s_line, btArgs.m_parserFrame->lineNumber
      )
    );
  }

  VMRegAnchor _;
  if (!vmfp()) {
    // If there are no VM frames, we're done
    return bt;
  }

  int depth = 0;
  ActRec* fp = nullptr;
  Offset pc = 0;

  // Get the fp and pc of the top frame (possibly skipping one frame)
  {
    if (btArgs.m_skipTop) {
      fp = g_context->getPrevVMState(vmfp(), &pc);
      if (!fp) {
        // We skipped over the only VM frame, we're done
        return bt;
      }
    } else {
      fp = vmfp();
      Unit *unit = vmfp()->m_func->unit();
      assert(unit);
      pc = unit->offsetOf(vmpc());
    }

    // Handle the top frame
    if (btArgs.m_withSelf) {
      // Builtins don't have a file and line number
      if (!fp->m_func->isBuiltin()) {
        Unit* unit = fp->m_func->unit();
        assert(unit);
        const char* filename = fp->m_func->filename()->data();
        Offset off = pc;

        ArrayInit frame(btArgs.m_parserFrame ? 4 : 2, ArrayInit::Map{});
        frame.set(s_file, filename);
        frame.set(s_line, unit->getLineNumber(off));
        if (btArgs.m_parserFrame) {
          frame.set(s_function, s_include);
          frame.set(s_args, Array::Create(btArgs.m_parserFrame->filename));
        }
        bt.append(frame.toVariant());
        depth++;
      }
    }
  }
  // Handle the subsequent VM frames
  Offset prevPc = 0;
  for (ActRec* prevFp = g_context->getPrevVMState(fp, &prevPc);
       fp != nullptr && (btArgs.m_limit == 0 || depth < btArgs.m_limit);
       fp = prevFp, pc = prevPc,
         prevFp = g_context->getPrevVMState(fp, &prevPc)) {
    // do not capture frame for HPHP only functions
    if (fp->m_func->isNoInjection()) {
      continue;
    }

    ArrayInit frame(7, ArrayInit::Map{});

    auto const curUnit = fp->m_func->unit();
    auto const curOp = *reinterpret_cast<const Op*>(curUnit->at(pc));
    auto const isReturning =
      curOp == Op::RetC || curOp == Op::RetV ||
      curOp == Op::CreateCont || curOp == Op::Await ||
      fp->localsDecRefd();

    // Builtins and generators don't have a file and line number
    if (prevFp && !prevFp->m_func->isBuiltin() && !fp->resumed()) {
      auto const prevUnit = prevFp->m_func->unit();
      auto prevFile = prevUnit->filepath();
      if (prevFp->m_func->originalFilename()) {
        prevFile = prevFp->m_func->originalFilename();
      }
      assert(prevFile);
      frame.set(s_file, const_cast<StringData*>(prevFile));

      // In the normal method case, the "saved pc" for line number printing is
      // pointing at the cell conversion (Unbox/Pop) instruction, not the call
      // itself. For multi-line calls, this instruction is associated with the
      // subsequent line which results in an off-by-n. We're subtracting one
      // in order to look up the line associated with the FCall/FCallArray
      // instruction. Exception handling and the other opcodes (ex. BoxR)
      // already do the right thing. The emitter associates object access with
      // the subsequent expression and this would be difficult to modify.
      auto const opAtPrevPc =
        *reinterpret_cast<const Op*>(prevUnit->at(prevPc));
      Offset pcAdjust = 0;
      if (opAtPrevPc == OpPopR || opAtPrevPc == OpUnboxR) {
        pcAdjust = 1;
      }
      frame.set(s_line,
                prevFp->m_func->unit()->getLineNumber(prevPc - pcAdjust));
    }

    // check for include
    String funcname = const_cast<StringData*>(fp->m_func->name());
    if (fp->m_func->isClosureBody()) {
      static StringData* s_closure_label =
        makeStaticString("{closure}");
      funcname = s_closure_label;
    }

    // check for pseudomain
    if (funcname.empty()) {
      if (!prevFp) continue;
      funcname = s_include;
    }

    frame.set(s_function, funcname);

    if (!funcname.same(s_include)) {
      // Closures have an m_this but they aren't in object context
      Class* ctx = arGetContextClass(fp);
      if (ctx != nullptr && !fp->m_func->isClosureBody()) {
        frame.set(s_class, ctx->name()->data());
        if (fp->hasThis() && !isReturning) {
          if (btArgs.m_withThis) {
            frame.set(s_object, Object(fp->getThis()));
          }
          frame.set(s_type, "->");
        } else {
          frame.set(s_type, "::");
        }
      }
    }

    Array args = Array::Create();
    if (btArgs.m_ignoreArgs) {
      // do nothing
    } else if (funcname.same(s_include)) {
      if (depth) {
        args.append(const_cast<StringData*>(curUnit->filepath()));
        frame.set(s_args, args);
      }
    } else if (!RuntimeOption::EnableArgsInBacktraces || isReturning) {
      // Provide an empty 'args' array to be consistent with hphpc
      frame.set(s_args, args);
    } else {
      const int nparams = fp->m_func->numNonVariadicParams();
      int nargs = fp->numArgs();
      int nformals = std::min(nparams, nargs);

      if (UNLIKELY(fp->hasVarEnv() && fp->getVarEnv()->getFP() != fp)) {
        // VarEnv is attached to eval or debugger frame, other than the current
        // frame. Access locals thru VarEnv.
        auto varEnv = fp->getVarEnv();
        auto func = fp->func();
        for (int i = 0; i < nformals; i++) {
          TypedValue *arg = varEnv->lookup(func->localVarName(i));
          args.append(tvAsVariant(arg));
        }
      } else {
        for (int i = 0; i < nformals; i++) {
          TypedValue *arg = frame_local(fp, i);
          args.append(tvAsVariant(arg));
        }
      }

      /* builtin extra args are not stored in varenv */
      if (nargs > nparams && fp->hasExtraArgs()) {
        for (int i = nparams; i < nargs; i++) {
          TypedValue *arg = fp->getExtraArg(i - nparams);
          args.append(tvAsVariant(arg));
        }
      }
      frame.set(s_args, args);
    }

    bt.append(frame.toVariant());
    depth++;
  }
  return bt;

}