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(); }
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; }
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(); }
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; }
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; } }
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; } }
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; }
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(); }
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(); }
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; }
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()); }
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); }
Variant BaseVector::at(CVarRef key) { if (key.isInteger()) { return tvAsCVarRef(at(key.toInt64())); } throwBadKeyType(); return uninit_null(); }
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; }
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(¶m), 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); }
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; }
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; }
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(); }
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); }
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; }
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(); }
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(); }
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(); }
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; }
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); }
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(); } }