2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_FUNC_INL_H_
18 #error "func-inl.h should only be included by func.h"
21 #include "hphp/runtime/vm/unit-util.h"
24 ///////////////////////////////////////////////////////////////////////////////
28 void EHEnt::serde(SerDe
& sd
) {
29 folly::Optional
<Offset
> end
;
30 if (!SerDe::deserializing
) {
31 end
= (m_end
== kInvalidOffset
) ? folly::none
: folly::make_optional(m_end
);
42 if (SerDe::deserializing
) {
43 m_end
= end
.value_or(kInvalidOffset
);
47 ///////////////////////////////////////////////////////////////////////////////
50 inline Func::ParamInfo::ParamInfo()
51 : defaultValue(make_tv
<KindOfUninit
>())
55 inline void Func::ParamInfo::serde(SerDe
& sd
) {
67 inline bool Func::ParamInfo::hasDefaultValue() const {
68 return funcletOff
!= kInvalidOffset
;
71 inline bool Func::ParamInfo::hasScalarDefaultValue() const {
72 return hasDefaultValue() && defaultValue
.m_type
!= KindOfUninit
;
75 inline bool Func::ParamInfo::isInOut() const {
76 return flags
& (1 << static_cast<int32_t>(Flags::InOut
));
79 inline bool Func::ParamInfo::isVariadic() const {
80 return flags
& (1 << static_cast<int32_t>(Flags::Variadic
));
83 inline bool Func::ParamInfo::isNativeArg() const {
84 return flags
& (1 << static_cast<int32_t>(Flags::NativeArg
));
87 inline bool Func::ParamInfo::isTakenAsVariant() const {
88 return flags
& (1 << static_cast<int32_t>(Flags::AsVariant
));
91 inline bool Func::ParamInfo::isTakenAsTypedValue() const {
92 return flags
& (1 << static_cast<int32_t>(Flags::AsTypedValue
));
95 inline void Func::ParamInfo::setFlag(Func::ParamInfo::Flags flag
) {
96 flags
|= 1 << static_cast<int32_t>(flag
);
99 ///////////////////////////////////////////////////////////////////////////////
102 inline const void* Func::mallocEnd() const {
103 return reinterpret_cast<const char*>(this)
104 + Func::prologueTableOff()
105 + numPrologues() * sizeof(m_prologueTable
[0]);
108 inline bool Func::validate() const {
110 assertx(m_magic
== kMagic
);
112 assertx(m_name
!= nullptr);
116 ///////////////////////////////////////////////////////////////////////////////
117 // FuncId manipulation.
119 inline FuncId
Func::getFuncId() const {
120 assertx(!m_funcId
.isInvalid());
121 assertx(fromFuncId(m_funcId
) == this);
129 ///////////////////////////////////////////////////////////////////////////////
132 inline Unit
* Func::unit() const {
136 inline Class
* Func::cls() const {
137 return !isMethCaller() ? m_u
.cls() : nullptr;
140 inline PreClass
* Func::preClass() const {
141 return shared()->m_preClass
;
144 inline Class
* Func::baseCls() const {
145 return !(m_baseCls
& kMethCallerBit
) ?
146 reinterpret_cast<Class
*>(m_baseCls
) : nullptr;
149 inline Class
* Func::implCls() const {
150 return isClosureBody() ? baseCls() : cls();
153 inline const StringData
* Func::name() const {
154 assertx(m_name
!= nullptr);
158 inline String
Func::nameWithClosureName() const {
159 if (!isClosureBody()) return String(const_cast<StringData
*>(name()));
160 // Strip the file hash from the closure name.
161 String name
{const_cast<StringData
*>(baseCls()->name())};
162 return name
.substr(0, name
.find(';'));
165 inline StrNR
Func::nameStr() const {
166 assertx(m_name
!= nullptr);
167 return StrNR(m_name
);
170 inline size_t Func::stableHash() const {
171 return folly::hash::hash_combine(
172 name()->hashStatic(),
173 cls() ? cls()->name()->hashStatic() : 0,
178 inline const StringData
* Func::fullName() const {
179 if (m_fullName
== nullptr) return m_name
;
180 if (UNLIKELY((intptr_t)m_fullName
.get() == kNeedsFullName
)) {
181 m_fullName
= makeStaticString(
182 std::string(cls()->name()->data()) + "::" + m_name
->data());
187 inline String
Func::fullNameWithClosureName() const {
188 if (!isClosureBody()) return String(const_cast<StringData
*>(fullName()));
189 return nameWithClosureName();
192 inline StrNR
Func::fullNameStr() const {
193 assertx(m_fullName
!= nullptr);
194 return StrNR(fullName());
197 inline void invalidFuncConversion(const char* type
) {
198 SystemLib::throwInvalidOperationExceptionObject(folly::sformat(
199 "Cannot convert func to {}", type
203 inline NamedEntity
* Func::getNamedEntity() {
204 assertx(!shared()->m_preClass
);
205 return *reinterpret_cast<LowPtr
<NamedEntity
>*>(&m_namedEntity
);
208 inline const NamedEntity
* Func::getNamedEntity() const {
209 assertx(!shared()->m_preClass
);
210 return *reinterpret_cast<const LowPtr
<const NamedEntity
>*>(&m_namedEntity
);
213 inline void Func::setNamedEntity(const NamedEntity
* e
) {
214 *reinterpret_cast<LowPtr
<const NamedEntity
>*>(&m_namedEntity
) = e
;
217 inline const StringData
* Func::methCallerClsName() const {
218 assertx(isMethCaller() && isBuiltin());
222 inline const StringData
* Func::methCallerMethName() const {
223 assertx(isMethCaller() && isBuiltin() &&
224 (m_methCallerMethName
& kMethCallerBit
));
225 return reinterpret_cast<StringData
*>(m_methCallerMethName
- kMethCallerBit
);
228 ///////////////////////////////////////////////////////////////////////////////
231 inline const StringData
* Func::originalFilename() const {
232 return shared()->m_originalFilename
;
235 inline const StringData
* Func::filename() const {
236 // Builtins don't have filenames
238 return staticEmptyString();
241 // Use the original filename if it exists, otherwise grab the filename from
243 const StringData
* name
= originalFilename();
246 name
= m_unit
->filepath();
252 inline int Func::sn() const {
253 auto const sd
= shared();
254 auto small
= sd
->m_sn
;
255 if (UNLIKELY(small
== kSmallDeltaLimit
)) {
256 assertx(extShared());
257 return static_cast<const ExtendedSharedData
*>(sd
)->m_sn
;
262 inline int Func::line1() const {
263 return shared()->m_line1
;
266 inline int Func::line2() const {
267 auto const sd
= shared();
268 auto const delta
= sd
->m_line2Delta
;
269 if (UNLIKELY(delta
== kSmallDeltaLimit
)) {
270 assertx(extShared());
271 return static_cast<const ExtendedSharedData
*>(sd
)->m_line2
;
273 return line1() + delta
;
276 inline const StringData
* Func::docComment() const {
277 auto const ex
= extShared();
278 return ex
? ex
->m_docComment
: staticEmptyString();
281 ///////////////////////////////////////////////////////////////////////////////
284 inline PC
Func::entry() const {
285 const auto pc
= shared()->m_bc
.load(std::memory_order_relaxed
);
289 return const_cast<Func
*>(this)->loadBytecode();
292 inline Offset
Func::bclen() const {
293 return shared()->bclen();
296 inline Offset
Func::SharedData::bclen() const {
297 auto const len
= this->m_bclenSmall
;
298 if (UNLIKELY(len
== kSmallDeltaLimit
)) {
299 assertx(m_allFlags
.m_hasExtendedSharedData
);
300 return static_cast<const ExtendedSharedData
*>(this)->m_bclen
;
305 inline bool Func::contains(PC pc
) const {
306 return uintptr_t(pc
- entry()) < bclen();
309 inline bool Func::contains(Offset offset
) const {
310 assertx(offset
>= 0);
311 return offset
< bclen();
314 inline PC
Func::at(Offset off
) const {
315 // We don't use contains because we want to allow past becase it is often
317 assertx(off
>= 0 && off
<= bclen());
318 return entry() + off
;
321 inline Offset
Func::offsetOf(PC pc
) const {
322 assertx(contains(pc
));
326 inline Op
Func::getOp(Offset instrOffset
) const {
327 assertx(contains(instrOffset
));
328 return peek_op(entry() + instrOffset
);
331 inline Offset
Func::ctiEntry() const {
332 return shared()->m_cti_base
.load(std::memory_order_acquire
);
335 inline void Func::setCtiFunclet(int i
, Offset cti_funclet
) {
336 shared()->m_params
[i
].ctiFunclet
= cti_funclet
;
339 inline void Func::setCtiEntry(Offset base
, uint32_t size
) {
341 sd
->m_cti_size
= size
;
342 sd
->m_cti_base
.store(base
, std::memory_order_release
);
345 ///////////////////////////////////////////////////////////////////////////////
348 inline MaybeDataType
Func::hniReturnType() const {
349 auto const ex
= extShared();
350 return ex
? ex
->m_hniReturnType
: folly::none
;
353 inline RepoAuthType
Func::repoReturnType() const {
354 return shared()->m_repoReturnType
;
357 inline RepoAuthType
Func::repoAwaitedReturnType() const {
358 return shared()->m_repoAwaitedReturnType
;
361 inline bool Func::isReturnByValue() const {
362 return shared()->m_allFlags
.m_returnByValue
;
365 inline const TypeConstraint
& Func::returnTypeConstraint() const {
366 return shared()->m_retTypeConstraint
;
369 inline const StringData
* Func::returnUserType() const {
370 return shared()->m_retUserType
;
373 inline bool Func::hasReturnWithMultiUBs() const {
374 return shared()->m_allFlags
.m_hasReturnWithMultiUBs
;
377 inline const Func::UpperBoundVec
& Func::returnUBs() const {
378 assertx(hasReturnWithMultiUBs());
379 return extShared()->m_returnUBs
;
382 ///////////////////////////////////////////////////////////////////////////////
385 inline const Func::ParamInfoVec
& Func::params() const {
386 return shared()->m_params
;
389 inline uint32_t Func::numParams() const {
390 assertx(bool(m_attrs
& AttrVariadicParam
) != bool(m_paramCounts
& 1));
391 assertx((m_paramCounts
>> 1) == params().size());
392 return (m_paramCounts
) >> 1;
395 inline uint32_t Func::numNonVariadicParams() const {
396 assertx(bool(m_attrs
& AttrVariadicParam
) != bool(m_paramCounts
& 1));
397 assertx((m_paramCounts
>> 1) == params().size());
398 return (m_paramCounts
- 1) >> 1;
401 inline uint32_t Func::numRequiredParams() const {
402 for (auto i
= numNonVariadicParams(); i
> 0; --i
) {
403 if (!params()[i
- 1].hasDefaultValue()) return i
;
408 inline bool Func::hasVariadicCaptureParam() const {
410 assertx(bool(m_attrs
& AttrVariadicParam
) ==
411 (numParams() && params()[numParams() - 1].isVariadic()));
413 return m_attrs
& AttrVariadicParam
;
416 inline bool Func::hasParamsWithMultiUBs() const {
417 return shared()->m_allFlags
.m_hasParamsWithMultiUBs
;
420 inline const Func::ParamUBMap
& Func::paramUBs() const {
421 assertx(hasParamsWithMultiUBs());
422 return extShared()->m_paramUBs
;
425 ///////////////////////////////////////////////////////////////////////////////
426 // Locals, iterators, and stack.
428 inline int Func::numLocals() const {
429 return shared()->m_numLocals
;
432 inline int Func::numIterators() const {
433 return shared()->m_numIterators
;
436 inline Id
Func::numNamedLocals() const {
437 return shared()->m_localNames
.size();
440 inline Id
Func::coeffectsLocalId() const {
441 assertx(hasCoeffectsLocal());
442 return numParams() + (hasReifiedGenerics() ? 1 : 0);
445 inline Id
Func::reifiedGenericsLocalId() const {
446 assertx(hasReifiedGenerics());
450 inline const StringData
* Func::localVarName(Id id
) const {
452 return id
< numNamedLocals() ? shared()->m_localNames
[id
] : nullptr;
455 inline LowStringPtr
const* Func::localNames() const {
456 return shared()->m_localNames
.accessList();
459 inline int Func::maxStackCells() const {
460 return m_maxStackCells
;
463 inline int Func::numSlotsInFrame() const {
465 shared()->m_numLocals
+
466 shared()->m_numIterators
* (sizeof(Iter
) / sizeof(TypedValue
));
469 inline bool Func::hasForeignThis() const {
470 return m_hasForeignThis
;
473 inline void Func::setHasForeignThis(bool hasForeignThis
) {
474 m_hasForeignThis
= hasForeignThis
;
477 inline void Func::setGenerated(bool isGenerated
) {
478 shared()->m_allFlags
.m_isGenerated
= isGenerated
;
481 ///////////////////////////////////////////////////////////////////////////////
482 // Definition context.
484 inline bool Func::isMethod() const {
485 return (bool)baseCls();
488 inline bool Func::isFromTrait() const {
489 return m_attrs
& AttrTrait
;
492 inline bool Func::isPublic() const {
493 return m_attrs
& AttrPublic
;
496 inline bool Func::isStatic() const {
497 return m_attrs
& AttrStatic
;
500 inline bool Func::isStaticInPrologue() const {
501 return isStatic() && !isClosureBody();
504 inline bool Func::hasThisInBody() const {
505 return cls() && !isStatic();
508 inline bool Func::hasNoContextAttr() const {
509 return m_attrs
& AttrNoContext
;
512 inline bool Func::isAbstract() const {
513 return m_attrs
& AttrAbstract
;
516 inline bool Func::isPreFunc() const {
520 inline bool Func::isMemoizeWrapper() const {
521 return shared()->m_allFlags
.m_isMemoizeWrapper
;
524 inline bool Func::isMemoizeWrapperLSB() const {
525 return shared()->m_allFlags
.m_isMemoizeWrapperLSB
;
528 inline bool Func::isMemoizeImpl() const {
529 return isMemoizeImplName(name());
532 inline const StringData
* Func::memoizeImplName() const {
533 assertx(isMemoizeWrapper());
534 return genMemoizeImplName(name());
537 ///////////////////////////////////////////////////////////////////////////////
540 inline bool Func::isBuiltin() const {
541 return m_attrs
& AttrBuiltin
;
544 inline bool Func::isCPPBuiltin() const {
545 auto const ex
= extShared();
546 return UNLIKELY(!!ex
) && ex
->m_arFuncPtr
;
549 inline ArFunction
Func::arFuncPtr() const {
550 if (auto const ex
= extShared()) return ex
->m_arFuncPtr
;
554 inline NativeFunction
Func::nativeFuncPtr() const {
555 if (auto const ex
= extShared()) return ex
->m_nativeFuncPtr
;
559 ///////////////////////////////////////////////////////////////////////////////
562 inline bool Func::isClosureBody() const {
563 return shared()->m_allFlags
.m_isClosureBody
;
566 ///////////////////////////////////////////////////////////////////////////////
569 inline bool Func::isAsync() const {
570 return shared()->m_allFlags
.m_isAsync
;
573 inline bool Func::isGenerator() const {
574 return shared()->m_allFlags
.m_isGenerator
;
577 inline bool Func::isPairGenerator() const {
578 return shared()->m_allFlags
.m_isPairGenerator
;
581 inline bool Func::isAsyncFunction() const {
582 return isAsync() && !isGenerator();
585 inline bool Func::isNonAsyncGenerator() const {
586 return !isAsync() && isGenerator();
589 inline bool Func::isAsyncGenerator() const {
590 return isAsync() && isGenerator();
593 inline bool Func::isResumable() const {
594 return isAsync() || isGenerator();
597 ///////////////////////////////////////////////////////////////////////////////
600 inline RuntimeCoeffects
Func::requiredCoeffects() const {
601 return m_requiredCoeffects
;
604 inline RuntimeCoeffects
Func::shallowCoeffectsWithLocals() const {
605 return extShared() ? extShared()->m_shallowCoeffectsWithLocals
606 : RuntimeCoeffects::none();
609 inline void Func::setRequiredCoeffects(RuntimeCoeffects c
) {
610 m_requiredCoeffects
= c
;
613 inline StaticCoeffectNamesMap
Func::staticCoeffectNames() const {
614 return shared()->m_staticCoeffectNames
;
617 inline bool Func::isRxDisabled() const {
618 return shared()->m_allFlags
.m_isRxDisabled
;
621 inline bool Func::hasCoeffectsLocal() const {
622 return hasCoeffectRules() &&
623 !(getCoeffectRules().size() == 1 &&
624 getCoeffectRules()[0].isGeneratorThis());
627 inline bool Func::hasCoeffectRules() const {
628 return attrs() & AttrHasCoeffectRules
;
631 inline const Func::CoeffectRules
& Func::getCoeffectRules() const {
632 assertx(extShared());
633 assertx(hasCoeffectRules());
634 return extShared()->m_coeffectRules
;
637 ///////////////////////////////////////////////////////////////////////////////
640 inline Slot
Func::methodSlot() const {
645 inline bool Func::hasPrivateAncestor() const {
646 return m_hasPrivateAncestor
;
649 ///////////////////////////////////////////////////////////////////////////////
652 inline bool Func::isGenerated() const {
653 return shared()->m_allFlags
.m_isGenerated
;
656 inline bool Func::isSpecial(const StringData
* name
) {
657 return strncmp("86", name
->data(), 2) == 0;
660 ///////////////////////////////////////////////////////////////////////////////
663 inline Attr
Func::attrs() const {
667 inline const UserAttributeMap
& Func::userAttributes() const {
668 return shared()->m_userAttributes
;
671 inline bool Func::isUnique() const {
672 return m_attrs
& AttrUnique
;
675 inline bool Func::isPersistent() const {
676 return m_attrs
& AttrPersistent
;
679 inline bool Func::isInterceptable() const {
680 return m_attrs
& AttrInterceptable
;
683 inline bool Func::isNoInjection() const {
684 return m_attrs
& AttrNoInjection
;
687 inline bool Func::isSkipFrame() const {
688 return isCPPBuiltin() || (isBuiltin() && !isMethod());
691 inline bool Func::isProvenanceSkipFrame() const {
692 return m_attrs
& AttrProvenanceSkipFrame
;
695 inline bool Func::isFoldable() const {
696 return m_attrs
& AttrIsFoldable
;
699 inline bool Func::supportsAsyncEagerReturn() const {
700 return m_attrs
& AttrSupportsAsyncEagerReturn
;
703 inline bool Func::isDynamicallyCallable() const {
704 return m_attrs
& AttrDynamicallyCallable
;
707 inline folly::Optional
<int64_t> Func::dynCallSampleRate() const {
708 if (auto const ex
= extShared()) {
709 if (ex
->m_dynCallSampleRate
>= 0) return ex
->m_dynCallSampleRate
;
714 inline bool Func::isMethCaller() const {
715 return m_attrs
& AttrIsMethCaller
;
718 inline bool Func::isPhpLeafFn() const {
719 return shared()->m_allFlags
.m_isPhpLeafFn
;
722 inline bool Func::hasReifiedGenerics() const {
723 return shared()->m_allFlags
.m_hasReifiedGenerics
;
726 ///////////////////////////////////////////////////////////////////////////////
727 // Unit table entries.
729 inline const Func::EHEntVec
& Func::ehtab() const {
730 return shared()->m_ehtab
;
733 inline const EHEnt
* Func::findEH(Offset o
) const {
734 assertx(o
>= 0 && o
< bclen());
735 return findEH(shared()->m_ehtab
, o
);
738 template<class Container
>
739 const typename
Container::value_type
*
740 Func::findEH(const Container
& ehtab
, Offset o
) {
741 const typename
Container::value_type
* eh
= nullptr;
743 for (uint32_t i
= 0, sz
= ehtab
.size(); i
< sz
; ++i
) {
744 if (ehtab
[i
].m_base
<= o
&& o
< ehtab
[i
].m_past
) {
751 ///////////////////////////////////////////////////////////////////////////////
754 inline rds::Handle
Func::funcHandle() const {
755 return m_cachedFunc
.handle();
758 inline unsigned char* Func::getFuncBody() const {
762 inline void Func::setFuncBody(unsigned char* fb
) {
766 inline uint8_t* Func::getPrologue(int index
) const {
767 return m_prologueTable
[index
];
770 inline void Func::setPrologue(int index
, unsigned char* tca
) {
771 m_prologueTable
[index
] = tca
;
774 ///////////////////////////////////////////////////////////////////////////////
777 inline int8_t& Func::maybeIntercepted() const {
778 return m_maybeIntercepted
;
781 ///////////////////////////////////////////////////////////////////////////////
784 inline void Func::setAttrs(Attr attrs
) {
788 inline void Func::setBaseCls(Class
* baseCls
) {
789 m_baseCls
= to_low(baseCls
);
792 inline void Func::setFuncHandle(rds::Link
<LowPtr
<Func
>,
793 rds::Mode::NonLocal
> l
) {
794 // TODO(#2950356): This assertion fails for create_function with an existing
795 // declared function named __lambda_func.
796 //assertx(!m_cachedFunc.valid());
800 inline void Func::setHasPrivateAncestor(bool b
) {
801 m_hasPrivateAncestor
= b
;
804 inline void Func::setMethodSlot(Slot s
) {
809 inline bool Func::serialize() const {
810 if (m_serialized
) return false;
811 const_cast<Func
*>(this)->m_serialized
= true;
815 //////////////////////////////////////////////////////////////////////
817 inline const Func::ExtendedSharedData
* Func::extShared() const {
818 return const_cast<Func
*>(this)->extShared();
821 inline Func::ExtendedSharedData
* Func::extShared() {
822 auto const s
= shared();
823 return UNLIKELY(s
->m_allFlags
.m_hasExtendedSharedData
)
824 ? static_cast<ExtendedSharedData
*>(s
)
828 ///////////////////////////////////////////////////////////////////////////////