hphp/runtime/vm/func-inl.h (606 lines of code) (raw):
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_VM_FUNC_INL_H_
#error "func-inl.h should only be included by func.h"
#endif
#include "hphp/runtime/vm/unit-util.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// EH table.
template<class SerDe>
void EHEnt::serde(SerDe& sd) {
Optional<Offset> end;
if (!SerDe::deserializing) {
end = (m_end == kInvalidOffset) ? std::nullopt : make_optional(m_end);
}
sd(m_base)
(m_past)
(m_iterId)
(m_handler)
(end)
(m_parentIndex)
;
if (SerDe::deserializing) {
m_end = end.value_or(kInvalidOffset);
}
}
///////////////////////////////////////////////////////////////////////////////
// ParamInfo.
inline Func::ParamInfo::ParamInfo()
: defaultValue(make_tv<KindOfUninit>())
{}
template<class SerDe>
inline void Func::ParamInfo::serde(SerDe& sd) {
sd(builtinType)
(funcletOff)
(defaultValue)
(phpCode)
(typeConstraint)
(flags)
(userAttributes)
(userType)
;
}
inline bool Func::ParamInfo::hasDefaultValue() const {
return funcletOff != kInvalidOffset;
}
inline bool Func::ParamInfo::hasScalarDefaultValue() const {
return hasDefaultValue() && defaultValue.m_type != KindOfUninit;
}
inline bool Func::ParamInfo::isInOut() const {
return flags & (1 << static_cast<int32_t>(Flags::InOut));
}
inline bool Func::ParamInfo::isReadonly() const {
return flags & (1 << static_cast<int32_t>(Flags::Readonly));
}
inline bool Func::ParamInfo::isVariadic() const {
return flags & (1 << static_cast<int32_t>(Flags::Variadic));
}
inline bool Func::ParamInfo::isNativeArg() const {
return flags & (1 << static_cast<int32_t>(Flags::NativeArg));
}
inline bool Func::ParamInfo::isTakenAsVariant() const {
return flags & (1 << static_cast<int32_t>(Flags::AsVariant));
}
inline bool Func::ParamInfo::isTakenAsTypedValue() const {
return flags & (1 << static_cast<int32_t>(Flags::AsTypedValue));
}
inline void Func::ParamInfo::setFlag(Func::ParamInfo::Flags flag) {
flags |= 1 << static_cast<int32_t>(flag);
}
///////////////////////////////////////////////////////////////////////////////
// Func.
inline const void* Func::mallocEnd() const {
return reinterpret_cast<const char*>(this)
+ Func::prologueTableOff()
+ numPrologues() * sizeof(m_prologueTable[0]);
}
inline bool Func::validate() const {
#ifndef NDEBUG
assertx(m_magic == kMagic);
#endif
assertx(m_name != nullptr);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// FuncId manipulation.
inline FuncId Func::getFuncId() const {
#ifdef USE_LOWPTR
assertx(fromFuncId(FuncId{this}) == this);
return FuncId{this};
#else
assertx(!m_funcId.isInvalid());
assertx(fromFuncId(m_funcId) == this);
return m_funcId;
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Basic info.
inline Unit* Func::unit() const {
return m_unit;
}
inline Class* Func::cls() const {
return !isMethCaller() ? m_u.cls() : nullptr;
}
inline PreClass* Func::preClass() const {
return shared()->m_preClass;
}
inline Class* Func::baseCls() const {
return !(m_baseCls & kMethCallerBit) ?
reinterpret_cast<Class*>(m_baseCls) : nullptr;
}
inline Class* Func::implCls() const {
return isClosureBody() ? baseCls() : cls();
}
inline const StringData* Func::name() const {
assertx(m_name != nullptr);
return m_name;
}
inline String Func::nameWithClosureName() const {
if (!isClosureBody()) return String(const_cast<StringData*>(name()));
// Strip the file hash from the closure name.
String name{const_cast<StringData*>(baseCls()->name())};
auto const pos = name.find(';');
if (pos < 0) return name;
return name.substr(0, pos);
}
inline StrNR Func::nameStr() const {
assertx(m_name != nullptr);
return StrNR(m_name);
}
inline size_t Func::stableHash() const {
return folly::hash::hash_combine(
name()->hashStatic(),
cls() ? cls()->name()->hashStatic() : 0,
unit()->sn()
);
}
inline const StringData* Func::fullName() const {
if (m_fullName == nullptr) return m_name;
if (UNLIKELY((intptr_t)m_fullName.get() == kNeedsFullName)) {
m_fullName = makeStaticString(
std::string(cls()->name()->data()) + "::" + m_name->data());
}
return m_fullName;
}
inline String Func::fullNameWithClosureName() const {
if (!isClosureBody()) return String(const_cast<StringData*>(fullName()));
return nameWithClosureName();
}
inline StrNR Func::fullNameStr() const {
assertx(m_fullName != nullptr);
return StrNR(fullName());
}
inline void invalidFuncConversion(const char* type) {
SystemLib::throwInvalidOperationExceptionObject(folly::sformat(
"Cannot convert func to {}", type
));
}
inline NamedEntity* Func::getNamedEntity() {
assertx(!shared()->m_preClass);
return *reinterpret_cast<LowPtr<NamedEntity>*>(&m_namedEntity);
}
inline const NamedEntity* Func::getNamedEntity() const {
assertx(!shared()->m_preClass);
return *reinterpret_cast<const LowPtr<const NamedEntity>*>(&m_namedEntity);
}
inline void Func::setNamedEntity(const NamedEntity* e) {
*reinterpret_cast<LowPtr<const NamedEntity>*>(&m_namedEntity) = e;
}
inline const StringData* Func::methCallerClsName() const {
assertx(isMethCaller() && isBuiltin());
return m_u.name();
}
inline const StringData* Func::methCallerMethName() const {
assertx(isMethCaller() && isBuiltin() &&
(m_methCallerMethName & kMethCallerBit));
return reinterpret_cast<StringData*>(m_methCallerMethName - kMethCallerBit);
}
///////////////////////////////////////////////////////////////////////////////
// File info.
inline const StringData* Func::originalFilename() const {
return shared()->m_originalFilename;
}
inline const StringData* Func::filename() const {
// Builtins don't have filenames
if (isBuiltin()) {
return staticEmptyString();
}
// Use the original filename if it exists, otherwise grab the filename from
// the unit
const StringData* name = originalFilename();
if (!name) {
assertx(m_unit);
name = m_unit->filepath();
assertx(name);
}
return name;
}
inline int Func::sn() const {
auto const sd = shared();
auto small = sd->m_sn;
if (UNLIKELY(small == kSmallDeltaLimit)) {
assertx(extShared());
return static_cast<const ExtendedSharedData*>(sd)->m_sn;
}
return small;
}
inline int Func::line1() const {
return shared()->m_line1;
}
inline int Func::line2() const {
auto const sd = shared();
auto const delta = sd->m_line2Delta;
if (UNLIKELY(delta == kSmallDeltaLimit)) {
assertx(extShared());
return static_cast<const ExtendedSharedData*>(sd)->m_line2;
}
return line1() + delta;
}
inline const StringData* Func::docComment() const {
auto const ex = extShared();
return ex ? ex->m_docComment : staticEmptyString();
}
///////////////////////////////////////////////////////////////////////////////
// Bytecode.
inline PC Func::entry() const {
auto const bc = shared()->m_bc.copy();
return bc.isPtr() ? bc.ptr() : const_cast<Func*>(this)->loadBytecode();
}
inline Offset Func::bclen() const {
return shared()->bclen();
}
inline Offset Func::SharedData::bclen() const {
auto const len = this->m_bclenSmall;
if (UNLIKELY(len == kSmallDeltaLimit)) {
assertx(m_allFlags.m_hasExtendedSharedData);
return static_cast<const ExtendedSharedData*>(this)->m_bclen;
}
return len;
}
inline bool Func::contains(PC pc) const {
return uintptr_t(pc - entry()) < bclen();
}
inline bool Func::contains(Offset offset) const {
assertx(offset >= 0);
return offset < bclen();
}
inline PC Func::at(Offset off) const {
// We don't use contains because we want to allow past becase it is often
// used in loops
assertx(off >= 0 && off <= bclen());
return entry() + off;
}
inline Offset Func::offsetOf(PC pc) const {
assertx(contains(pc));
return pc - entry();
}
inline Op Func::getOp(Offset instrOffset) const {
assertx(contains(instrOffset));
return peek_op(entry() + instrOffset);
}
inline Offset Func::ctiEntry() const {
return shared()->m_cti_base.load(std::memory_order_acquire);
}
inline void Func::setCtiFunclet(int i, Offset cti_funclet) {
shared()->m_params[i].ctiFunclet = cti_funclet;
}
inline void Func::setCtiEntry(Offset base, uint32_t size) {
auto sd = shared();
sd->m_cti_size = size;
sd->m_cti_base.store(base, std::memory_order_release);
}
///////////////////////////////////////////////////////////////////////////////
// Return type.
inline MaybeDataType Func::hniReturnType() const {
auto const ex = extShared();
return ex ? ex->m_hniReturnType : std::nullopt;
}
inline RepoAuthType Func::repoReturnType() const {
return shared()->m_repoReturnType;
}
inline RepoAuthType Func::repoAwaitedReturnType() const {
return shared()->m_repoAwaitedReturnType;
}
inline bool Func::isReturnByValue() const {
return shared()->m_allFlags.m_returnByValue;
}
inline const TypeConstraint& Func::returnTypeConstraint() const {
return shared()->m_retTypeConstraint;
}
inline const StringData* Func::returnUserType() const {
return shared()->m_retUserType;
}
inline bool Func::hasReturnWithMultiUBs() const {
return shared()->m_allFlags.m_hasReturnWithMultiUBs;
}
inline const Func::UpperBoundVec& Func::returnUBs() const {
assertx(hasReturnWithMultiUBs());
return extShared()->m_returnUBs;
}
///////////////////////////////////////////////////////////////////////////////
// Parameters.
inline const Func::ParamInfoVec& Func::params() const {
return shared()->m_params;
}
inline uint32_t Func::numParams() const {
assertx(bool(m_attrs & AttrVariadicParam) != bool(m_paramCounts & 1));
assertx((m_paramCounts >> 1) == params().size());
return (m_paramCounts) >> 1;
}
inline uint32_t Func::numNonVariadicParams() const {
assertx(bool(m_attrs & AttrVariadicParam) != bool(m_paramCounts & 1));
assertx((m_paramCounts >> 1) == params().size());
return (m_paramCounts - 1) >> 1;
}
inline uint32_t Func::numRequiredParams() const {
for (auto i = numNonVariadicParams(); i > 0; --i) {
if (!params()[i - 1].hasDefaultValue()) return i;
}
return 0;
}
inline bool Func::hasVariadicCaptureParam() const {
#ifndef NDEBUG
assertx(bool(m_attrs & AttrVariadicParam) ==
(numParams() && params()[numParams() - 1].isVariadic()));
#endif
return m_attrs & AttrVariadicParam;
}
inline uint64_t Func::inOutBits() const {
return m_inoutBits;
}
inline bool Func::takesInOutParams() const {
return m_inoutBits != 0;
}
inline bool Func::hasParamsWithMultiUBs() const {
return shared()->m_allFlags.m_hasParamsWithMultiUBs;
}
inline const Func::ParamUBMap& Func::paramUBs() const {
assertx(hasParamsWithMultiUBs());
return extShared()->m_paramUBs;
}
///////////////////////////////////////////////////////////////////////////////
// Locals, iterators, and stack.
inline int Func::numLocals() const {
return shared()->m_numLocals;
}
inline int Func::numIterators() const {
return shared()->m_numIterators;
}
inline Id Func::numNamedLocals() const {
return shared()->m_localNames.size();
}
inline uint32_t Func::reifiedGenericsLocalId() const {
assertx(hasReifiedGenerics());
return numParams();
}
inline uint32_t Func::coeffectsLocalId() const {
assertx(hasCoeffectsLocal());
auto id = numParams();
if (hasReifiedGenerics()) ++id;
return id;
}
inline uint32_t Func::numFuncEntryInputs() const {
auto id = numParams();
if (hasReifiedGenerics()) ++id;
if (hasCoeffectsLocal()) ++id;
return id;
}
inline uint32_t Func::firstClosureUseLocalId() const {
assertx(isClosureBody());
return numFuncEntryInputs();
}
inline uint32_t Func::firstRegularLocalId() const {
auto id = numParams();
if (hasReifiedGenerics()) ++id;
if (hasCoeffectsLocal()) ++id;
if (isClosureBody()) id += numClosureUseLocals();
return id;
}
inline const StringData* Func::localVarName(Id id) const {
assertx(id >= 0);
return id < numNamedLocals() ? shared()->m_localNames[id] : nullptr;
}
inline LowStringPtr const* Func::localNames() const {
return shared()->m_localNames.accessList();
}
inline int Func::maxStackCells() const {
return m_maxStackCells;
}
inline int Func::numSlotsInFrame() const {
return
shared()->m_numLocals +
shared()->m_numIterators * (sizeof(Iter) / sizeof(TypedValue));
}
inline bool Func::hasForeignThis() const {
return m_hasForeignThis;
}
inline void Func::setHasForeignThis(bool hasForeignThis) {
m_hasForeignThis = hasForeignThis;
}
inline void Func::setGenerated(bool isGenerated) {
shared()->m_allFlags.m_isGenerated = isGenerated;
}
///////////////////////////////////////////////////////////////////////////////
// Definition context.
inline bool Func::isMethod() const {
return (bool)baseCls();
}
inline bool Func::isFromTrait() const {
return m_attrs & AttrTrait;
}
inline bool Func::isPublic() const {
return m_attrs & AttrPublic;
}
inline bool Func::isStatic() const {
return m_attrs & AttrStatic;
}
inline bool Func::isStaticInPrologue() const {
return isStatic() && !isClosureBody();
}
inline bool Func::hasThisInBody() const {
return cls() && !isStatic();
}
inline bool Func::isAbstract() const {
return m_attrs & AttrAbstract;
}
inline bool Func::isPreFunc() const {
return m_isPreFunc;
}
inline bool Func::isMemoizeWrapper() const {
return shared()->m_allFlags.m_isMemoizeWrapper;
}
inline bool Func::isMemoizeWrapperLSB() const {
return shared()->m_allFlags.m_isMemoizeWrapperLSB;
}
inline bool Func::isPolicyShardedMemoize() const {
return shared()->m_allFlags.m_isPolicyShardedMemoize;
}
inline bool Func::isMemoizeImpl() const {
return isMemoizeImplName(name());
}
inline const StringData* Func::memoizeImplName() const {
assertx(isMemoizeWrapper());
return genMemoizeImplName(name());
}
inline size_t Func::numKeysForMemoize() const {
return numParams()
+ (hasReifiedGenerics() ? 1 : 0)
+ (RO::EvalEnableImplicitContext &&
(shared()->m_allFlags.m_isPolicyShardedMemoize) ? 1 : 0);
}
///////////////////////////////////////////////////////////////////////////////
// Builtins.
inline bool Func::isBuiltin() const {
return m_attrs & AttrBuiltin;
}
inline bool Func::isCPPBuiltin() const {
auto const ex = extShared();
return UNLIKELY(!!ex) && ex->m_arFuncPtr;
}
inline ArFunction Func::arFuncPtr() const {
if (auto const ex = extShared()) return ex->m_arFuncPtr;
return nullptr;
}
inline NativeFunction Func::nativeFuncPtr() const {
if (auto const ex = extShared()) return ex->m_nativeFuncPtr;
return nullptr;
}
///////////////////////////////////////////////////////////////////////////////
// Closures.
inline bool Func::isClosureBody() const {
return shared()->m_allFlags.m_isClosureBody;
}
///////////////////////////////////////////////////////////////////////////////
// Resumables.
inline bool Func::isAsync() const {
return shared()->m_allFlags.m_isAsync;
}
inline bool Func::isGenerator() const {
return shared()->m_allFlags.m_isGenerator;
}
inline bool Func::isPairGenerator() const {
return shared()->m_allFlags.m_isPairGenerator;
}
inline bool Func::isAsyncFunction() const {
return isAsync() && !isGenerator();
}
inline bool Func::isNonAsyncGenerator() const {
return !isAsync() && isGenerator();
}
inline bool Func::isAsyncGenerator() const {
return isAsync() && isGenerator();
}
inline bool Func::isResumable() const {
return isAsync() || isGenerator();
}
///////////////////////////////////////////////////////////////////////////////
// Coeffects.
inline RuntimeCoeffects Func::requiredCoeffects() const {
return m_requiredCoeffects;
}
inline RuntimeCoeffects Func::coeffectEscapes() const {
return extShared() ? extShared()->m_coeffectEscapes
: RuntimeCoeffects::none();
}
inline void Func::setRequiredCoeffects(RuntimeCoeffects c) {
m_requiredCoeffects = c;
}
inline StaticCoeffectNamesMap Func::staticCoeffectNames() const {
return shared()->m_staticCoeffectNames;
}
inline bool Func::hasCoeffectsLocal() const {
return hasCoeffectRules() &&
!(getCoeffectRules().size() == 1 &&
getCoeffectRules()[0].isGeneratorThis());
}
inline bool Func::hasCoeffectRules() const {
return attrs() & AttrHasCoeffectRules;
}
inline const Func::CoeffectRules& Func::getCoeffectRules() const {
assertx(extShared());
assertx(hasCoeffectRules());
return extShared()->m_coeffectRules;
}
///////////////////////////////////////////////////////////////////////////////
// Methods.
inline Slot Func::methodSlot() const {
assertx(isMethod());
return m_methodSlot;
}
inline bool Func::hasPrivateAncestor() const {
return m_hasPrivateAncestor;
}
///////////////////////////////////////////////////////////////////////////////
// Magic methods.
inline bool Func::isGenerated() const {
return shared()->m_allFlags.m_isGenerated;
}
inline bool Func::isSpecial(const StringData* name) {
return strncmp("86", name->data(), 2) == 0;
}
///////////////////////////////////////////////////////////////////////////////
// Other attributes.
inline Attr Func::attrs() const {
return m_attrs;
}
inline const UserAttributeMap& Func::userAttributes() const {
return shared()->m_userAttributes;
}
inline bool Func::isUnique() const {
return m_attrs & AttrUnique;
}
inline bool Func::isPersistent() const {
return m_attrs & AttrPersistent;
}
inline bool Func::isInterceptable() const {
return m_attrs & AttrInterceptable;
}
inline bool Func::isNoInjection() const {
return m_attrs & AttrNoInjection;
}
inline bool Func::isSkipFrame() const {
return isCPPBuiltin() || (isBuiltin() && !isMethod());
}
inline bool Func::isProvenanceSkipFrame() const {
return m_attrs & AttrProvenanceSkipFrame;
}
inline bool Func::isFoldable() const {
return m_attrs & AttrIsFoldable;
}
inline bool Func::supportsAsyncEagerReturn() const {
return m_attrs & AttrSupportsAsyncEagerReturn;
}
inline bool Func::isDynamicallyCallable() const {
return m_attrs & AttrDynamicallyCallable;
}
inline Optional<int64_t> Func::dynCallSampleRate() const {
if (auto const ex = extShared()) {
if (ex->m_dynCallSampleRate >= 0) return ex->m_dynCallSampleRate;
}
return std::nullopt;
}
inline bool Func::isMethCaller() const {
return m_attrs & AttrIsMethCaller;
}
inline bool Func::isPhpLeafFn() const {
return shared()->m_allFlags.m_isPhpLeafFn;
}
inline bool Func::hasReifiedGenerics() const {
return shared()->m_allFlags.m_hasReifiedGenerics;
}
///////////////////////////////////////////////////////////////////////////////
// Unit table entries.
inline const Func::EHEntVec& Func::ehtab() const {
return shared()->m_ehtab;
}
inline const EHEnt* Func::findEH(Offset o) const {
assertx(o >= 0 && o < bclen());
return findEH(shared()->m_ehtab, o);
}
template<class Container>
const typename Container::value_type*
Func::findEH(const Container& ehtab, Offset o) {
const typename Container::value_type* eh = nullptr;
for (uint32_t i = 0, sz = ehtab.size(); i < sz; ++i) {
if (ehtab[i].m_base <= o && o < ehtab[i].m_past) {
eh = &ehtab[i];
}
}
return eh;
}
///////////////////////////////////////////////////////////////////////////////
// JIT data.
inline jit::TCA Func::getFuncEntry() const {
return m_funcEntry;
}
inline void Func::setFuncEntry(jit::TCA funcEntry) {
m_funcEntry = funcEntry;
}
inline uint8_t* Func::getPrologue(int index) const {
return m_prologueTable[index];
}
inline void Func::setPrologue(int index, unsigned char* tca) {
m_prologueTable[index] = tca;
}
///////////////////////////////////////////////////////////////////////////////
// Other methods.
inline bool Func::maybeIntercepted() const {
return atomicFlags().check(Func::Flags::MaybeIntercepted);
}
inline void Func::setMaybeIntercepted() {
atomicFlags().set(Func::Flags::MaybeIntercepted);
}
///////////////////////////////////////////////////////////////////////////////
// Public setters.
inline void Func::setAttrs(Attr attrs) {
m_attrs = attrs;
}
inline void Func::setBaseCls(Class* baseCls) {
m_baseCls = to_low(baseCls);
}
inline void Func::setHasPrivateAncestor(bool b) {
m_hasPrivateAncestor = b;
}
inline void Func::setMethodSlot(Slot s) {
assertx(isMethod());
m_methodSlot = s;
}
//////////////////////////////////////////////////////////////////////
inline const Func::ExtendedSharedData* Func::extShared() const {
return const_cast<Func*>(this)->extShared();
}
inline Func::ExtendedSharedData* Func::extShared() {
auto const s = shared();
return UNLIKELY(s->m_allFlags.m_hasExtendedSharedData)
? static_cast<ExtendedSharedData*>(s)
: nullptr;
}
///////////////////////////////////////////////////////////////////////////////
}