Beispiel #1
0
static Array get_frame_args_with_ref(const ActRec* ar) {
  int numNonVariadic = ar->func()->numNonVariadicParams();
  int numArgs = ar->numArgs();

  PackedArrayInit retArray(numArgs);

  auto local = reinterpret_cast<TypedValue*>(
    uintptr_t(ar) - sizeof(TypedValue)
  );
  int i = 0;
  // The function's formal parameters are on the stack
  for (; i < numArgs && i < numNonVariadic; ++i) {
    retArray.appendWithRef(tvAsCVarRef(local));
    --local;
  }

  if (i < numArgs) {
    // If there are still args that haven't been accounted for, they have
    // either been ... :
    if (ar->func()->hasVariadicCaptureParam()) {
      // ... shuffled into a packed array stored in the variadic capture
      // param on the stack
      for (ArrayIter iter(tvAsCVarRef(local)); iter; ++iter) {
        retArray.appendWithRef(iter.secondRef());
      }
    } else {
      // ... or moved into the ExtraArgs datastructure.
      for (; i < numArgs; ++i) {
        retArray.appendWithRef(
          tvAsCVarRef(ar->getExtraArg(i - numNonVariadic)));
      }
    }
  }
  return retArray.toArray();
}
Beispiel #2
0
ArrayData *VectorArray::dequeue(Variant &value) {
  if (UNLIKELY(!m_size)) {
    value.setNull();
    return nullptr;
  }
  if (UNLIKELY(getCount() > 1)) {
    value = tvAsCVarRef(&m_elems[0]);
    if (m_size == 1) {
      return StaticEmptyVectorArray::Get();
    }
    VectorArray *a = NEW(VectorArray)(this, 1, m_size - 1);
    a->m_pos = (ssize_t)0;
    return a;
  }
  value = tvAsCVarRef(&m_elems[0]);
  m_size--;
  tvAsVariant(&m_elems[0]).~Variant();
  for (uint i = 0; i < m_size; i++) {
    // TypedValue copy without refcounting.
    m_elems[i] = m_elems[i+1];
  }
  // To match PHP-like semantics, the dequeue operation resets the array's
  // internal iterator
  m_pos = m_size ? (ssize_t)0 : ArrayData::invalid_index;
  return nullptr;
}
Beispiel #3
0
void BaseMap::addAllImpl(const Variant& iterable) {
  if (iterable.isNull()) return;
  VMRegGuard _;

  decltype(cap()) oldCap = 0;
  bool ok = IterateKV(
    *iterable.asTypedValue(),
    [&](ArrayData* adata) {
      auto sz = adata->size();
      if (!sz) return true;
      if (!m_size) {
        if (adata->isMixed()) {
          replaceArray(adata);
          updateIntLikeStrKeys();
          return true;
        }
      } else {
        oldCap = cap(); // assume minimal collisions
      }
      reserve(m_size + sz);
      mutateAndBump();
      return false;
    },
    [this](const TypedValue* key, const TypedValue* value) {
      setRaw(tvAsCVarRef(key), tvAsCVarRef(value));
    },
    [this](ObjectData* coll) {
      switch (coll->collectionType()) {
        case CollectionType::Map:
        case CollectionType::Set:
        {
          if (m_size) break;
          auto hc = static_cast<HashCollection*>(coll);
          replaceArray(hc->arrayData());
          setIntLikeStrKeys(BaseMap::intLikeStrKeys(hc));
          return true;
        }
        case CollectionType::Pair:
          mutateAndBump();
          break;
        default:
          break;
      }
      return false;
    },
    [this](const TypedValue* key, const TypedValue* value) {
      set(tvAsCVarRef(key), tvAsCVarRef(value));
    });

  if (UNLIKELY(!ok)) {
    throw_invalid_collection_parameter();
  }
  // ... and shrink back if that was incorrect
  if (oldCap) shrinkIfCapacityTooHigh(oldCap);
}
Variant HHVM_FUNCTION(get_class_vars, const String& className) {
  const Class* cls = Unit::loadClass(className.get());
  if (!cls) {
    return false;
  }
  cls->initialize();


  auto const propInfo = cls->declProperties();

  auto const numDeclProps = cls->numDeclProperties();
  auto const numSProps    = cls->numStaticProperties();

  // The class' instance property initialization template is in different
  // places, depending on whether it has any request-dependent initializers
  // (i.e. constants)
  auto const& declPropInitVec = cls->declPropInit();
  auto const propVals = !cls->pinitVec().empty()
    ? cls->getPropData()
    : &declPropInitVec;

  assert(propVals != nullptr);
  assert(propVals->size() == numDeclProps);

  // For visibility checks
  CallerFrame cf;
  auto ctx = arGetContextClass(cf());

  ArrayInit arr(numDeclProps + numSProps, ArrayInit::Map{});

  for (size_t i = 0; i < numDeclProps; ++i) {
    auto const name = const_cast<StringData*>(propInfo[i].name.get());
    // Empty names are used for invisible/private parent properties; skip them.
    assert(name->size() != 0);
    if (Class::IsPropAccessible(propInfo[i], ctx)) {
      auto const value = &((*propVals)[i]);
      arr.set(name, tvAsCVarRef(value));
    }
  }

  for (auto const& sprop : cls->staticProperties()) {
    auto const lookup = cls->getSProp(ctx, sprop.name);
    if (lookup.accessible) {
      arr.set(
        const_cast<StringData*>(sprop.name.get()),
        tvAsCVarRef(lookup.prop)
      );
    }
  }

  return arr.toArray();
}
Beispiel #5
0
ArrayData* ProxyArray::SetInt(ArrayData* ad,
                              int64_t k,
                              Cell v,
                              bool copy) {
  if (copy) {
    return innerArr(ad)->set(k, tvAsCVarRef(&v), true);
  }
  auto const r = innerArr(ad)->set(
    k, tvAsCVarRef(&v), innerArr(ad)->cowCheck()
  );
  reseatable(ad, r);
  return ad;
}
Beispiel #6
0
ArrayData* ProxyArray::SetStr(ArrayData* ad,
                              StringData* k,
                              Cell v,
                              bool copy) {
  if (copy) {
    return innerArr(ad)->set(k, tvAsCVarRef(&v), copy);
  } else {
    auto r = innerArr(ad)->set(k,
        tvAsCVarRef(&v), innerArr(ad)->hasMultipleRefs());
    reseatable(ad, r);
    return ad;
  }
}
Beispiel #7
0
ArrayData* ProxyArray::SetInt(ArrayData* ad,
                              int64_t k,
                              Cell v,
                              bool copy) {
  if (copy) {
    return innerArr(ad)->set(k, tvAsCVarRef(&v), true);
  } else {
    auto r = innerArr(ad)->set(k,
        tvAsCVarRef(&v), innerArr(ad)->hasMultipleRefs());
    reseatable(ad, r);
    return ad;
  }
}
Beispiel #8
0
ArrayData* ProxyArray::SetStr(ArrayData* ad,
                              StringData* k,
                              Cell v,
                              bool copy) {
  if (copy) {
    return innerArr(ad)->set(k, tvAsCVarRef(&v), copy);
  }
  auto const r = innerArr(ad)->set(
    k, tvAsCVarRef(&v), innerArr(ad)->cowCheck()
  );
  reseatable(ad, r);
  return ad;
}
Beispiel #9
0
ArrayData* deepCopyDict(ArrayData* arr) {
  assert(arr->isDict());
  DictInit ai{arr->size()};
  MixedArray::IterateKV(
    MixedArray::asMixed(arr),
    [&](const TypedValue* k, const TypedValue* v) {
      Variant value{tvAsCVarRef(v)};
      deepCopy(value.asTypedValue());
      ai.setValidKey(tvAsCVarRef(k), value);
      return false;
    }
  );
  return ai.create();
}
Beispiel #10
0
ArrayData* deepCopyArray(ArrayData* arr) {
  assert(arr->isPHPArray());
  ArrayInit ai(arr->size(), ArrayInit::Mixed{});
  IterateKV(
    arr,
    [&](const TypedValue* k, const TypedValue* v) {
      Variant value{tvAsCVarRef(v)};
      deepCopy(value.asTypedValue());
      ai.setValidKey(tvAsCVarRef(k), value);
      return false;
    }
  );
  return ai.create();
}
Beispiel #11
0
int shuffleArgsForMagicCall(ActRec* ar) {
  if (!ar->hasInvName()) {
    return 0;
  }
  const Func* f UNUSED = ar->m_func;
  f->validate();
  assert(f->name()->isame(s_call.get())
         || f->name()->isame(s_callStatic.get()));
  assert(f->numParams() == 2);
  assert(ar->hasInvName());
  StringData* invName = ar->getInvName();
  assert(invName);
  ar->setVarEnv(nullptr);
  int nargs = ar->numArgs();

  // We need to make an array containing all the arguments passed by the
  // caller and put it where the second argument is
  PackedArrayInit aInit(nargs);
  for (int i = 0; i < nargs; ++i) {
    auto const tv = reinterpret_cast<TypedValue*>(
      uintptr_t(ar) - (i+1) * sizeof(TypedValue)
    );
    aInit.append(tvAsCVarRef(tv));
    tvRefcountedDecRef(tv);
  }

  // Put invName in the slot for first argument
  setArgInActRec(ar, 0, uint64_t(invName), KindOfString);
  // Put argArray in the slot for second argument
  auto const argArray = aInit.toArray().detach();
  setArgInActRec(ar, 1, uint64_t(argArray), KindOfArray);
  // Fix up ActRec's numArgs
  ar->initNumArgs(2);
  return 1;
}
Beispiel #12
0
void ArrayIter::secondHelper(Variant & v) {
    if (hasVector()) {
        c_Vector* vec = getVector();
        if (UNLIKELY(m_versionNumber != vec->getVersionNumber())) {
            throw_collection_modified();
        }
        v = tvAsCVarRef(vec->at(m_pos));
        return;
    }
    if (hasMap()) {
        c_Map* mp = getMap();
        if (UNLIKELY(m_versionNumber != mp->getVersionNumber())) {
            throw_collection_modified();
        }
        v = mp->iter_value(m_pos);
        return;
    }
    if (hasStableMap()) {
        c_StableMap* smp = getStableMap();
        if (UNLIKELY(m_versionNumber != smp->getVersionNumber())) {
            throw_collection_modified();
        }
        v = smp->iter_value(m_pos);
        return;
    }
    assert(hasObject());
    ObjectData* obj = getObject();
    v = obj->o_invoke(s_current, Array());
}
Beispiel #13
0
HOT_FUNC
Variant ArrayIter::second() {
    if (hasVector()) {
        c_Vector* vec = getVector();
        if (UNLIKELY(m_versionNumber != vec->getVersionNumber())) {
            throw_collection_modified();
        }
        return tvAsCVarRef(vec->at(m_pos));
    }
    if (hasMap()) {
        c_Map* mp = getMap();
        if (UNLIKELY(m_versionNumber != mp->getVersionNumber())) {
            throw_collection_modified();
        }
        return mp->iter_value(m_pos);
    }
    if (hasStableMap()) {
        c_StableMap* smp = getStableMap();
        if (UNLIKELY(m_versionNumber != smp->getVersionNumber())) {
            throw_collection_modified();
        }
        return smp->iter_value(m_pos);
    }
    if (hasObject()) {
        ObjectData* obj = getObject();
        return obj->o_invoke(s_current, Array());
    }
    assert(hasArrayData());
    assert(m_pos != ArrayData::invalid_index);
    const ArrayData* ad = getArrayData();
    assert(ad);
    return ad->getValue(m_pos);
}
Beispiel #14
0
Variant BaseVector::at(CVarRef key) {
  if (key.isInteger()) {
    return tvAsCVarRef(at(key.toInt64()));
  }
  throwBadKeyType();
  return uninit_null();
}
Beispiel #15
0
HOT_FUNC_HPHP
CVarRef VectorArray::get(int64 k, bool error /* = false */) const {
  if (LIKELY(inRange(k, m_size))) {
    return tvAsCVarRef(&m_elems[k]);
  }
  return error ? getNotFound(k) : null_variant;
}
Beispiel #16
0
TypedValue HHVM_FUNCTION(serialize_memoize_param, TypedValue param) {
  // Memoize throws in the emitter if any function parameters are references, so
  // we can just assert that the param is cell here
  assertx(param.m_type != KindOfRef);
  auto const type = param.m_type;

  if (isStringType(type)) {
    auto const str = param.m_data.pstr;
    if (str->empty()) {
      return make_tv<KindOfPersistentString>(s_emptyStrMemoKey.get());
    } else if ((unsigned char)str->data()[0] < '~') {
      // fb_compact_serialize always returns a string with the high-bit set in
      // the first character. Furthermore, we use ~ to begin all our special
      // constants, so anything less than ~ can't collide. There's no worry
      // about int-like strings because we use dicts (which don't perform key
      // coercion) to store the memoized values.
      str->incRefCount();
      return param;
    }
  } else if (isContainer(param) && getContainerSize(param) == 0) {
    return make_tv<KindOfPersistentString>(s_emptyArrMemoKey.get());
  } else if (type == KindOfUninit || type == KindOfNull) {
    return make_tv<KindOfPersistentString>(s_nullMemoKey.get());
  } else if (type == KindOfBoolean) {
    return make_tv<KindOfPersistentString>(
      param.m_data.num ? s_trueMemoKey.get() : s_falseMemoKey.get()
    );
  } else if (type == KindOfInt64) {
    return param;
  }

  return tvReturn(
    fb_compact_serialize(tvAsCVarRef(&param),
                         FBCompactSerializeBehavior::MemoizeParam));
}
void EventHook::RunUserProfiler(const ActRec* ar, int mode) {
  // Don't do anything if we are running the profiling function itself
  // or if we haven't set up a profiler.
  if (g_vmContext->m_executingSetprofileCallback ||
      g_vmContext->m_setprofileCallback.isNull()) {
    return;
  }
  Transl::VMRegAnchor _;
  ExecutingSetprofileCallbackGuard guard;

  Array params;
  Array frameinfo;

  if (mode == ProfileEnter) {
    params.append(s_enter);
    frameinfo.set(s_args, hhvm_get_frame_args(ar));
  } else {
    params.append(s_exit);
    if (!g_vmContext->m_faults.empty()) {
      Fault fault = g_vmContext->m_faults.back();
      if (fault.m_faultType == Fault::UserException) {
        frameinfo.set(s_exception, fault.m_userException);
      }
    } else if (!ar->m_func->isBuiltin() &&
               !ar->m_func->isGenerator()) {
      // TODO (#1131400) This is wrong for builtins
      frameinfo.set(s_return, tvAsCVarRef(g_vmContext->m_stack.topTV()));
    }
  }

  params.append(VarNR(ar->m_func->fullName()));
  params.append(frameinfo);

  f_call_user_func_array(g_vmContext->m_setprofileCallback, params);
}
Beispiel #18
0
Variant HHVM_FUNCTION(constant, const String& name) {
  if (!name.get()) return init_null();
  const char *data = name.data();
  int len = name.length();

  char *colon;
  if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') {
    // class constant
    int classNameLen = colon - data;
    char *constantName = colon + 2;
    Class* cls = getClassByName(data, classNameLen);
    if (cls) {
      String cnsName(constantName, data + len - constantName, CopyString);
      Cell cns = cls->clsCnsGet(cnsName.get());
      if (cns.m_type != KindOfUninit) {
        return cellAsCVarRef(cns);
      }
    }
  } else {
    auto const cns = Unit::loadCns(name.get());
    if (cns) return tvAsCVarRef(cns);
  }

  raise_warning("constant(): Couldn't find constant %s", data);
  return init_null();
}
RefData* staticLocInitImpl(StringData* name, ActRec* fp, TypedValue val,
                           TargetCache::CacheHandle ch) {
  assert(useTargetCache == (bool)ch);
  HphpArray* map;
  if (useTargetCache) {
    // If we have a cache handle, we know the current func isn't a
    // closure or generator closure so we can directly grab its static
    // locals map.
    const Func* func = fp->m_func;
    assert(!(func->isClosureBody() || func->isGeneratorFromClosure()));
    map = func->getStaticLocals();
  } else {
    map = get_static_locals(fp);
  }

  TypedValue *mapVal = map->nvGet(name);
  if (!mapVal) {
    map->set(name, tvAsCVarRef(&val), false);
    mapVal = map->nvGet(name);
  }
  if (mapVal->m_type != KindOfRef) {
    tvBox(mapVal);
  }
  assert(mapVal->m_type == KindOfRef);
  RefData* ret = mapVal->m_data.pref;
  if (useTargetCache) {
    *TargetCache::handleToPtr<RefData*>(ch) = ret;
  }
  ret->incRefCount();
  return ret;
}
Beispiel #20
0
ZendArray *VectorArray::escalateToNonEmptyZendArray() const {
  assert(m_size);
  ZendArray *ret;
  ZendArray::Bucket *p[256], **pp;
  if (LIKELY(m_size < 256)) {
    pp = p;
  } else {
    pp =
      (ZendArray::Bucket **)malloc(sizeof(ZendArray::Bucket *) * (m_size + 1));
  }
  DECLARE_ALLOCATOR(a, ZendArray::Bucket, Bucket);
  for (int64 i = 0; i < m_size; i++) {
    CVarRef v = tvAsCVarRef(&m_elems[i]);
    pp[i] = NEWALLOC(a) ZendArray::Bucket(i, withRefBind(v));
  }
  pp[m_size] = nullptr;
  ret = NEW(ZendArray)(m_size, m_size, pp);
  if (UNLIKELY(pp != p)) free(pp);
  if (m_pos != ArrayData::invalid_index) {
    ret->setPosition(ret->getIndex(m_pos));
  } else {
    ret->setPosition(0);
  }
  return ret;
}
Beispiel #21
0
Array BaseVector::toArrayImpl() const {
  PackedArrayInit ai(m_size);
  uint sz = m_size;
  for (uint i = 0; i < sz; ++i) {
    ai.append(tvAsCVarRef(&m_data[i]));
  }
  return ai.toArray();
}
Beispiel #22
0
void RepoQuery::bindTypedValue(const char* paramName, const TypedValue& tv) {
  if (tv.m_type == KindOfUninit) {
    bindBlob(paramName, "", 0, true);
  } else {
    String blob = f_serialize(tvAsCVarRef(&tv));
    bindBlob(paramName, blob->data(), blob->size());
  }
}
ArrayData* addElemStringKeyHelper(ArrayData* ad,
                                  StringData* key,
                                  TypedValue value) {
  // this does not re-enter
  bool copy = ad->getCount() > 1;
  // set will decRef any old value that may have been overwritten
  // if appropriate
  int64_t intkey;
  ArrayData* retval = UNLIKELY(key->isStrictlyInteger(intkey)) ?
                      ad->set(intkey, tvAsCVarRef(&value), copy) :
                      ad->set(key, tvAsCVarRef(&value), copy);
  // TODO Task #1970153: It would be great if there were set()
  // methods that didn't bump up the refcount so that we didn't
  // have to decrement it here
  tvRefcountedDecRef(&value);
  return arrayRefShuffle<false>(ad, retval, nullptr);
}
Beispiel #24
0
HOT_FUNC_HPHP
Variant VectorArray::value(ssize_t &pos) const {
  if (pos >= 0 && pos < m_size) {
    return tvAsCVarRef(&m_elems[pos]);
  }
  pos = VectorArray::invalid_index;
  return false;
}
Beispiel #25
0
Array HashCollection::toValuesArray() {
  PackedArrayInit ai(m_size);
  auto* eLimit = elmLimit();
  for (auto* e = firstElm(); e != eLimit; e = nextElm(e, eLimit)) {
    ai.append(tvAsCVarRef(&e->data));
  }
  return ai.toArray();
}
Beispiel #26
0
ArrayData* ArrayCommon::ToDict(ArrayData* a, bool) {
  auto const size = a->size();
  if (!size) return staticEmptyDictArray();
  DictInit init{size};
  IterateKV(
    a,
    [&](const TypedValue* k, const TypedValue* v) {
      if (UNLIKELY(v->m_type == KindOfRef)) {
        if (v->m_data.pref->isReferenced()) {
          throwRefInvalidArrayValueException(init.toArray());
        }
      }
      init.setValidKey(tvAsCVarRef(k), tvAsCVarRef(v));
    }
  );
  return init.create();
}
Beispiel #27
0
ArrayData* deepCopyDict(ArrayData* arr) {
  assert(arr->isDict());
  Array ar(arr);
  MixedArray::IterateKV(
    MixedArray::asMixed(arr),
    [&](const TypedValue* k, const TypedValue* v) {
      if (!isRefcountedType(v->m_type)) return false;
      Variant value{tvAsCVarRef(v)};
      deepCopy(value.asTypedValue());
      if (value.asTypedValue()->m_data.num != v->m_data.num) {
        ar.set(tvAsCVarRef(k), value);
      }
      return false;
    }
  );
  return ar.detach();
}
Beispiel #28
0
int64_t BaseVector::linearsearch(CVarRef search_value) {
  uint sz = m_size;
  for (uint i = 0; i < sz; ++i) {
    if (same(search_value, tvAsCVarRef(&m_data[i]))) {
      return i;
    }
  }
  return -1;
}
Beispiel #29
0
HOT_FUNC
CVarRef SharedMap::getValueRef(ssize_t pos) const {
  SharedVariant *sv = getValueImpl(pos);
  DataType t = sv->getType();
  if (!IS_REFCOUNTED_TYPE(t)) return sv->asCVarRef();
  if (LIKELY(m_localCache != nullptr)) {
    assert(unsigned(pos) < size());
    TypedValue* tv = &m_localCache[pos];
    if (tv->m_type != KindOfUninit) return tvAsCVarRef(tv);
  } else {
    static_assert(KindOfUninit == 0, "must be 0 since we use smart_calloc");
    m_localCache = (TypedValue*) smart_calloc(size(), sizeof(TypedValue));
  }
  TypedValue* tv = &m_localCache[pos];
  tvAsVariant(tv) = sv->toLocal();
  assert(tv->m_type != KindOfUninit);
  return tvAsCVarRef(tv);
}
Beispiel #30
0
StringData* prepareAnyKey(TypedValue* tv) {
  if (IS_STRING_TYPE(tv->m_type)) {
    StringData* str = tv->m_data.pstr;
    str->incRefCount();
    return str;
  } else {
    return tvAsCVarRef(tv).toString().detach();
  }
}