HOT_FUNC_VM
int64_t decodeCufIterHelper(Iter* it, TypedValue func) {
  DECLARE_FRAME_POINTER(framePtr);

  ObjectData* obj = nullptr;
  HPHP::Class* cls = nullptr;
  StringData* invName = nullptr;

  auto ar = (ActRec*)framePtr->m_savedRbp;
  if (LIKELY(ar->m_func->isBuiltin())) {
    ar = g_vmContext->getOuterVMFrame(ar);
  }
  const Func* f = vm_decode_function(tvAsVariant(&func),
                                     ar, false,
                                     obj, cls, invName,
                                     false);
  if (UNLIKELY(!f)) return false;
  CufIter &cit = it->cuf();
  cit.setFunc(f);
  if (obj) {
    cit.setCtx(obj);
    obj->incRefCount();
  } else {
    cit.setCtx(cls);
  }
  cit.setName(invName);
  return true;
}
Example #2
0
/*
 * Helper method from converting between a PHP function and a CufIter.
 */
static bool vm_decode_function_cufiter(const Variant& function,
                                       SmartCufIterPtr& cufIter) {
  ObjectData* obj = nullptr;
  HPHP::Class* cls = nullptr;
  HPHP::JIT::CallerFrame cf;
  StringData* invName = nullptr;
  // Don't warn here, let the caller decide what to do if the func is nullptr.
  const HPHP::Func* func = vm_decode_function(function, cf(), false,
                                              obj, cls, invName, false);
  if (func == nullptr) {
    return false;
  }

  cufIter = smart::make_unique<CufIter>();
  cufIter->setFunc(func);
  cufIter->setName(invName);
  if (obj) {
    cufIter->setCtx(obj);
    obj->incRefCount();
  } else {
    cufIter->setCtx(cls);
  }

  return true;
}
Example #3
0
ALWAYS_INLINE
typename std::enable_if<
  std::is_base_of<BaseMap, TMap>::value, Object>::type
BaseMap::php_takeWhile(const Variant& fn) {
  CallCtx ctx;
  vm_decode_function(fn, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
               "Parameter must be a valid callback");
  }
  auto map = req::make<TMap>();
  if (!m_size) return Object{std::move(map)};
  int32_t version UNUSED;
  if (std::is_same<c_Map, TMap>::value) {
    version = m_version;
  }
  for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) {
    auto* e = iter_elm(pos);
    bool b = invokeAndCastToBool(ctx, 1, &e->data);
    if (std::is_same<c_Map, TMap>::value) {
      if (UNLIKELY(version != m_version)) {
        throw_collection_modified();
      }
    }
    if (!b) break;
    e = iter_elm(pos);
    if (e->hasIntKey()) {
      map->set(e->ikey, &e->data);
    } else {
      assert(e->hasStrKey());
      map->set(e->skey, &e->data);
    }
  }
  return Object{std::move(map)};
}
Example #4
0
void BaseVector::mapwithkey(BaseVector* bvec, CVarRef callback) {
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    Object e(SystemLib::AllocInvalidArgumentExceptionObject(
      "Parameter must be a valid callback"));
    throw e;
  }
  uint sz = m_size;
  bvec->reserve(sz);
  for (uint i = 0; i < sz; ++i) {
    TypedValue* tv = &bvec->m_data[i];
    int32_t version = m_version;
    TypedValue args[2] = {
      make_tv<KindOfInt64>(i),
      m_data[i]
    };
    g_vmContext->invokeFuncFew(tv, ctx, 2, args);
    if (UNLIKELY(version != m_version)) {
      tvRefcountedDecRef(tv);
      throw_collection_modified();
    }
    ++bvec->m_size;
  }
}
Example #5
0
void BaseVector::filterwithkey(BaseVector* bvec, CVarRef callback) {
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    Object e(SystemLib::AllocInvalidArgumentExceptionObject(
      "Parameter must be a valid callback"));
    throw e;
  }
  uint sz = m_size;
  for (uint i = 0; i < sz; ++i) {
    Variant ret;
    int32_t version = m_version;
    TypedValue args[2] = {
      make_tv<KindOfInt64>(i),
      m_data[i]
    };
    g_vmContext->invokeFuncFew(ret.asTypedValue(), ctx, 2, args);
    if (UNLIKELY(version != m_version)) {
      throw_collection_modified();
    }
    if (ret.toBoolean()) {
      bvec->add(&m_data[i]);
    }
  }
}
Example #6
0
Variant f_array_reduce(CVarRef input, CVarRef callback,
                       CVarRef initial /* = null_variant */) {
  getCheckedArray(input);
  CallCtx ctx;
  CallerFrame cf;
  vm_decode_function(callback, cf(), false, ctx);
  if (ctx.func == NULL) {
    return uninit_null();
  }
  return ArrayUtil::Reduce(arr_input, reduce_func, &ctx, initial);
}
Example #7
0
Variant f_array_filter(CVarRef input, CVarRef callback /* = null_variant */) {
  getCheckedArray(input);

  if (callback.isNull()) {
    return ArrayUtil::Filter(arr_input);
  }
  CallCtx ctx;
  EagerCallerFrame cf;
  vm_decode_function(callback, cf(), false, ctx);
  if (ctx.func == NULL) {
    return uninit_null();
  }
  return ArrayUtil::Filter(arr_input, filter_func, &ctx);
}
Example #8
0
bool f_ob_start(CVarRef callback /* = uninit_null() */,
                int chunk_size /* = 0 */, bool erase /* = true */) {
  // ignoring chunk_size and erase

  if (!callback.isNull()) {
    CallCtx ctx;
    vm_decode_function(callback, nullptr, false, ctx);
    if (!ctx.func) {
      return false;
    }
  }
  g_context->obStart(callback);
  return true;
}
Example #9
0
void HHVM_FUNCTION(disable_inlining, const Variant& function) {
  CallerFrame cf;
  ObjectData* obj = nullptr;
  HPHP::Class* cls = nullptr;
  StringData* invName = nullptr;
  const HPHP::Func* f = vm_decode_function(function, cf(), false, obj, cls,
                                               invName, false);
  if (f == nullptr || f->isAbstract()) {
    raise_warning("disable_inlining(): undefined function");
    return;
  }

  jit::InliningDecider::forbidInliningOf(f);
}
Example #10
0
void HHVM_STATIC_METHOD(IntlChar, enumCharTypes, const Variant& callback) {
  CallCtx ctx;
  ctx.func = nullptr;
  if (!callback.isNull()) {
    CallerFrame cf;
    vm_decode_function(callback, cf(), false, ctx);
  }
  if (!ctx.func) {
    s_intl_error->setError(U_INTERNAL_PROGRAM_ERROR,
                           "enumCharTypes callback failed");
    return;
  }
  u_enumCharTypes((UCharEnumTypeRange*)enumCharType_callback, &ctx);
}
Example #11
0
bool f_array_walk(VRefParam input, CVarRef funcname,
                  CVarRef userdata /* = null_variant */) {
  if (!input.isArray()) {
    throw_bad_array_exception();
    return false;
  }
  CallCtx ctx;
  CallerFrame cf;
  vm_decode_function(funcname, cf(), false, ctx);
  if (ctx.func == NULL) {
    return false;
  }
  ArrayUtil::Walk(input, walk_func, &ctx, false, NULL, userdata);
  return true;
}
Example #12
0
bool HHVM_FUNCTION(ob_start, const Variant& callback /* = null */,
                             int chunk_size /* = 0 */,
                             bool erase /* = true */) {
  // ignoring chunk_size and erase

  if (!callback.isNull()) {
    CallCtx ctx;
    vm_decode_function(callback, nullptr, false, ctx);
    if (!ctx.func) {
      return false;
    }
  }
  g_context->obStart(callback);
  return true;
}
Example #13
0
bool HHVM_FUNCTION(ob_start, const Variant& callback /* = null */,
                             int chunk_size /* = 0 */,
                             int flags /* = k_PHP_OUTPUT_HANDLER_STDFLAGS */) {
  // ignoring flags for now

  if (!callback.isNull()) {
    CallCtx ctx;
    vm_decode_function(callback, nullptr, false, ctx);
    if (!ctx.func) {
      return false;
    }
  }
  g_context->obStart(callback, chunk_size);
  return true;
}
Example #14
0
Variant vm_call_user_func(const Variant& function, const Variant& params,
                          bool forwarding /* = false */) {
  ObjectData* obj = nullptr;
  HPHP::Class* cls = nullptr;
  HPHP::JIT::CallerFrame cf;
  StringData* invName = nullptr;
  const HPHP::Func* f = vm_decode_function(function, cf(), forwarding,
                                           obj, cls, invName);
  if (f == nullptr || (!isContainer(params) && !params.isNull())) {
    return uninit_null();
  }
  Variant ret;
  g_context->invokeFunc((TypedValue*)&ret, f, params, obj, cls,
                          nullptr, invName, ExecutionContext::InvokeCuf);
  return ret;
}
Example #15
0
bool f_array_walk_recursive(VRefParam input, CVarRef funcname,
                            CVarRef userdata /* = null_variant */) {
  if (!input.isArray()) {
    throw_bad_array_exception();
    return false;
  }
  CallCtx ctx;
  CallerFrame cf;
  vm_decode_function(funcname, cf(), false, ctx);
  if (ctx.func == NULL) {
    return uninit_null();
  }
  PointerSet seen;
  ArrayUtil::Walk(input, walk_func, &ctx, true, &seen, userdata);
  return true;
}
Example #16
0
Object BaseMap::php_retain(const Variant& callback) {
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
               "Parameter must be a valid callback");
  }
  auto size = m_size;
  if (!size) { return Object{this}; }
  constexpr int64_t argc = useKey ? 2 : 1;
  TypedValue argv[argc];
  for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) {
    auto* e = iter_elm(pos);
    if (useKey) {
      if (e->hasIntKey()) {
        argv[0].m_type = KindOfInt64;
        argv[0].m_data.num = e->ikey;
      } else {
        argv[0].m_type = KindOfString;
        argv[0].m_data.pstr = e->skey;
      }
    }
    argv[argc-1] = e->data;
    int32_t version = m_version;
    bool b = invokeAndCastToBool(ctx, argc, argv);
    if (UNLIKELY(version != m_version)) {
      throw_collection_modified();
    }
    if (b) {
      continue;
    }
    mutateAndBump();
    version = m_version;
    e = iter_elm(pos);
    ssize_t pp = (e->hasIntKey()
                   ? findForRemove(e->ikey)
                   : findForRemove(e->skey, e->skey->hash()));
    eraseNoCompact(pp);
    if (UNLIKELY(version != m_version)) {
      throw_collection_modified();
    }
  }

  assert(m_size <= size);
  compactOrShrinkIfDensityTooLow();
  return Object{this};
}
Example #17
0
ALWAYS_INLINE
typename std::enable_if<
  std::is_base_of<BaseMap, TMap>::value, Object>::type
BaseMap::php_filter(const Variant& callback) const {
  VMRegGuard _;
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
               "Parameter must be a valid callback");
  }
  auto map = req::make<TMap>();
  if (!m_size) return Object(std::move(map));
  map->mutate();
  int32_t version = m_version;
  constexpr int64_t argc = useKey ? 2 : 1;
  TypedValue argv[argc];
  for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) {
    auto* e = iter_elm(pos);
    if (useKey) {
      if (e->hasIntKey()) {
        argv[0].m_type = KindOfInt64;
        argv[0].m_data.num = e->ikey;
      } else {
        argv[0].m_type = KindOfString;
        argv[0].m_data.pstr = e->skey;
      }
    }
    argv[argc-1] = e->data;
    bool b = invokeAndCastToBool(ctx, argc, argv);
    if (UNLIKELY(version != m_version)) {
      throw_collection_modified();
    }
    if (!b) continue;
    e = iter_elm(pos);
    if (e->hasIntKey()) {
      map->set(e->ikey, &e->data);
    } else {
      assert(e->hasStrKey());
      map->set(e->skey, &e->data);
    }
  }
  return Object(std::move(map));
}
Example #18
0
bool HHVM_FUNCTION(ob_start, const Variant& callback /* = null */,
                             int chunk_size /* = 0 */,
                             int flags /* = k_PHP_OUTPUT_HANDLER_STDFLAGS */) {
  // Note: the only flag which is implemented is FLUSHABLE

  if (!callback.isNull()) {
    CallCtx ctx;
    vm_decode_function(callback, nullptr, false, ctx);
    if (!ctx.func) {
      return false;
    }
  }
  OBFlags f = OBFlags::None;
  if (flags & k_PHP_OUTPUT_HANDLER_CLEANABLE) f |= OBFlags::Cleanable;
  if (flags & k_PHP_OUTPUT_HANDLER_FLUSHABLE) f |= OBFlags::Flushable;
  if (flags & k_PHP_OUTPUT_HANDLER_REMOVABLE) f |= OBFlags::Removable;
  g_context->obStart(callback, chunk_size, f);
  return true;
}
Example #19
0
void loadArrayFunctionContext(ArrayData* arr, ActRec* preLiveAR, ActRec* fp) {
  ObjectData* inst = nullptr;
  HPHP::Class* cls = nullptr;
  StringData* invName = nullptr;

  try {

    // if size != 2, throws exception
    // if the first element of the array is a string classname, will autoload it
    const Func* func = vm_decode_function(
      Variant(arr),
      fp,
      /* forwarding */ false,
      inst,
      cls,
      invName,
      /* warn */ false
    );
    if (UNLIKELY(func == nullptr)) {
      raise_error("Invalid callable (array)");
    }
    assert(cls != nullptr); // array should resolve only to a method

    preLiveAR->m_func = func;
    if (inst) {
      inst->incRefCount();
      preLiveAR->setThis(inst);
    } else {
      preLiveAR->setClass(cls);
    }
    if (UNLIKELY(invName != nullptr)) {
      preLiveAR->setInvName(invName);
    }
  } catch (...) {
    // This is extreme shadiness. See the comments of
    // arPreliveOverwriteCells() for more info on how this code gets the
    // unwinder to restore the pre-FPush state.
    auto firstActRecCell = arPreliveOverwriteCells(preLiveAR);
    firstActRecCell->m_type = KindOfArray;
    firstActRecCell->m_data.parr = arr;
    throw;
  }
}
typename std::enable_if<
  std::is_base_of<BaseSet, TSet>::value, Object>::type
BaseSet::php_skipWhile(const Variant& fn) {
  CallCtx ctx;
  vm_decode_function(fn, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
               "Parameter must be a valid callback");
  }
  auto set = req::make<TSet>();
  if (!m_size) return Object(std::move(set));
  // we don't reserve(), because we don't know how selective fn will be
  set->mutate();
  int32_t version UNUSED;
  if (std::is_same<c_Set, TSet>::value) {
    version = m_version;
  }
  uint32_t used = posLimit();
  uint32_t i = 0;
  for (; i < used; ++i) {
    if (isTombstone(i)) continue;
    Elm& e = data()[i];
    bool b = invokeAndCastToBool(ctx, 1, &e.data);
    if (std::is_same<c_Set, TSet>::value) {
      if (UNLIKELY(version != m_version)) {
        throw_collection_modified();
      }
    }
    if (!b) break;
  }
  for (; i < used; ++i) {
    if (isTombstone(i)) continue;
    Elm& e = data()[i];
    if (e.hasIntKey()) {
      set->addRaw(e.data.m_data.num);
    } else {
      assert(e.hasStrKey());
      set->addRaw(e.data.m_data.pstr);
    }
  }
  return Object(std::move(set));
}
Example #21
0
Variant f_array_map(int _argc, CVarRef callback, CVarRef arr1, CArrRef _argv /* = null_array */) {
  Array inputs;
  if (!arr1.isArray()) {
    throw_bad_array_exception();
    return uninit_null();
  }
  inputs.append(arr1);
  if (!_argv.empty()) {
    inputs = inputs.merge(_argv);
  }
  CallCtx ctx;
  ctx.func = NULL;
  if (!callback.isNull()) {
    EagerCallerFrame cf;
    vm_decode_function(callback, cf(), false, ctx);
  }
  if (ctx.func == NULL) {
    return ArrayUtil::Map(inputs, map_func, NULL);
  }
  return ArrayUtil::Map(inputs, map_func, &ctx);
}
Example #22
0
bool PackedArray::Usort(ArrayData* ad, const Variant& cmp_function) {
  assert(ad->isPacked());
  if (ad->m_size <= 1) {
    return true;
  }
  assert(!ad->hasMultipleRefs());
  if (UNLIKELY(strong_iterators_exist())) {
    free_strong_iterators(ad);
  }
  ElmUCompare<TVAccessor> comp;
  CallCtx ctx;
  CallerFrame cf;
  vm_decode_function(cmp_function, cf(), false, ctx);
  if (!ctx.func) {
    return false;
  }
  comp.ctx = &ctx;
  auto const data = packedData(ad);
  Sort::sort(data, data + ad->m_size, comp);
  return true;
}
typename std::enable_if<
  std::is_base_of<BaseSet, TSet>::value, Object>::type
BaseSet::php_map(const Variant& callback) const {
  VMRegGuard _;
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
      "Parameter must be a valid callback");
  }
  auto set = req::make<TSet>();
  if (!m_size) return Object{std::move(set)};
  assert(posLimit() != 0);
  assert(hashSize() > 0);
  assert(set->arrayData() == staticEmptyMixedArray());
  auto oldCap = set->cap();
  set->reserve(posLimit()); // presume minimum collisions ...
  assert(set->canMutateBuffer());
  constexpr int64_t argc = useKey ? 2 : 1;
  TypedValue argv[argc];
  for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) {
    auto e = iter_elm(pos);
    TypedValue tvCbRet;
    int32_t pVer = m_version;
    if (useKey) {
      argv[0] = e->data;
    }
    argv[argc-1] = e->data;
    g_context->invokeFuncFew(&tvCbRet, ctx, argc, argv);
    // Now that tvCbRet is live, make sure to decref even if we throw.
    SCOPE_EXIT { tvRefcountedDecRef(&tvCbRet); };
    if (UNLIKELY(m_version != pVer)) throw_collection_modified();
    set->addRaw(&tvCbRet);
  }
  // ... and shrink back if that was incorrect
  set->shrinkIfCapacityTooHigh(oldCap);
  return Object{std::move(set)};
}
typename std::enable_if<
  std::is_base_of<BaseSet, TSet>::value, Object>::type
BaseSet::php_filter(const Variant& callback) const {
  VMRegGuard _;
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
      "Parameter must be a valid callback");
  }
  auto set = req::make<TSet>();
  if (!m_size) return Object(std::move(set));
  // we don't reserve(), because we don't know how selective callback will be
  set->mutate();
  int32_t version = m_version;
  constexpr int64_t argc = useKey ? 2 : 1;
  TypedValue argv[argc];
  for (ssize_t pos = iter_begin(); iter_valid(pos); pos = iter_next(pos)) {
    auto e = iter_elm(pos);
    if (useKey) {
      argv[0] = e->data;
    }
    argv[argc-1] = e->data;
    bool b = invokeAndCastToBool(ctx, argc, argv);
    if (UNLIKELY(version != m_version)) {
      throw_collection_modified();
    }
    if (!b) continue;
    e = iter_elm(pos);
    if (e->hasIntKey()) {
      set->addRaw(e->data.m_data.num);
    } else {
      assert(e->hasStrKey());
      set->addRaw(e->data.m_data.pstr);
    }
  }
  return Object(std::move(set));
}
Example #25
0
void HHVM_STATIC_METHOD(IntlChar, enumCharNames,
                        const Variant& vStart, const Variant& vLimit,
                        const Variant& callback, int64_t choice) {
  GETCP_VOID(vStart, start);
  GETCP_VOID(vLimit, limit);
  CallCtx ctx;
  ctx.func = nullptr;
  if (!callback.isNull()) {
    CallerFrame cf;
    vm_decode_function(callback, cf(), false, ctx);
  }
  if (!ctx.func) {
    s_intl_error->setError(U_INTERNAL_PROGRAM_ERROR,
                           "enumCharNames callback failed");
    return;
  }

  UErrorCode error = U_ZERO_ERROR;
  u_enumCharNames(start, limit, (UEnumCharNamesFn*)enumCharNames_callback, &ctx,
                  (UCharNameChoice)choice, &error);
  if (U_FAILURE(error)) {
    s_intl_error->setError(error);
  }
}
Example #26
0
ALWAYS_INLINE
typename std::enable_if<
  std::is_base_of<BaseMap, TMap>::value, Object>::type
BaseMap::php_map(const Variant& callback) const {
  VMRegGuard _;
  CallCtx ctx;
  vm_decode_function(callback, nullptr, false, ctx);
  if (!ctx.func) {
    SystemLib::throwInvalidArgumentExceptionObject(
               "Parameter must be a valid callback");
  }
  auto map = req::make<TMap>();
  if (!m_size) return Object{std::move(map)};
  assert(posLimit() != 0);
  assert(hashSize() > 0);
  assert(map->arrayData() == staticEmptyMixedArray());
  map->m_arr = MixedArray::asMixed(MixedArray::MakeReserveMixed(cap()));
  map->setIntLikeStrKeys(intLikeStrKeys());
  wordcpy(map->hashTab(), hashTab(), hashSize());
  {
    uint32_t used = posLimit();
    int32_t version = m_version;
    uint32_t i = 0;
    // When the loop below finishes or when an exception is thrown,
    // make sure that posLimit() get set to the correct value and
    // that m_pos gets set to point to the first element.
    SCOPE_EXIT {
      map->setPosLimit(i);
      map->arrayData()->m_pos = map->nthElmPos(0);
    };
    constexpr int64_t argc = useKey ? 2 : 1;
    TypedValue argv[argc];
    for (; i < used; ++i) {
      const Elm& e = data()[i];
      Elm& ne = map->data()[i];
      if (isTombstone(i)) {
        ne.data.m_type = e.data.m_type;
        continue;
      }
      TypedValue* tv = &ne.data;
      if (useKey) {
        if (e.hasIntKey()) {
          argv[0].m_type = KindOfInt64;
          argv[0].m_data.num = e.ikey;
        } else {
          argv[0].m_type = KindOfString;
          argv[0].m_data.pstr = e.skey;
        }
      }
      argv[argc-1] = e.data;
      g_context->invokeFuncFew(tv, ctx, argc, argv);
      if (UNLIKELY(version != m_version)) {
        tvRefcountedDecRef(tv);
        throw_collection_modified();
      }
      if (e.hasStrKey()) {
        e.skey->incRefCount();
      }
      ne.ikey = e.ikey;
      ne.data.hash() = e.data.hash();
      map->incSize();
      // Needed so that the new elements are accounted for when GC scanning.
      map->incPosLimit();
    }
  }
  return Object{std::move(map)};
}