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_CLASS_INL_H_
18 #error "class-inl.h should only be included by class.h"
21 #include "hphp/runtime/base/runtime-error.h"
22 #include "hphp/runtime/base/strings.h"
25 ///////////////////////////////////////////////////////////////////////////////
27 inline bool Class::isZombie() const {
28 return !m_cachedClass
.bound();
32 inline bool Class::validate() const {
34 assertx(m_magic
== kMagic
);
36 assertx(name()->checkSane());
40 ///////////////////////////////////////////////////////////////////////////////
41 // Class::PropInitVec.
43 template <bool is_const
>
44 Class::PropInitVec::iterator_impl
<is_const
>::iterator_impl(
47 ) : m_val(tv
), m_bit(bit
) {}
49 template <bool is_const
>
50 bool Class::PropInitVec::iterator_impl
<is_const
>::operator==(
51 const Class::PropInitVec::iterator_impl
<is_const
>& o
53 return m_val
== o
.m_val
;
56 template <bool is_const
>
57 bool Class::PropInitVec::iterator_impl
<is_const
>::operator!=(
58 const Class::PropInitVec::iterator_impl
<is_const
>& o
63 template <bool is_const
>
64 Class::PropInitVec::iterator_impl
<is_const
>&
65 Class::PropInitVec::iterator_impl
<is_const
>::operator++() {
71 template <bool is_const
>
72 Class::PropInitVec::iterator_impl
<is_const
>
73 Class::PropInitVec::iterator_impl
<is_const
>::operator++(int) {
74 auto const ret
= *this;
79 template <bool is_const
>
80 Class::PropInitVec::Entry
<is_const
>
81 Class::PropInitVec::iterator_impl
<is_const
>::operator*() const {
82 return Entry
<is_const
>{m_val
, *m_bit
};
85 template <bool is_const
>
86 Class::PropInitVec::Entry
<is_const
>
87 Class::PropInitVec::iterator_impl
<is_const
>::operator->() const {
91 inline Class::PropInitVec::PropInitVec() : m_data(nullptr),
96 inline Class::PropInitVec::Entry
<false>
97 Class::PropInitVec::operator[](T i
) {
98 auto lval
= m_data
->at(i
);
99 auto deepInit
= deepInitBits()[i
];
100 return Entry
<false>{lval
, deepInit
};
103 template <typename T
>
104 inline Class::PropInitVec::Entry
<true>
105 Class::PropInitVec::operator[](T i
) const {
106 auto lval
= m_data
->at(i
);
107 auto deepInit
= deepInitBits()[i
];
108 return Entry
<true>{lval
, deepInit
};
111 inline Class::PropInitVec::iterator
Class::PropInitVec::begin() {
112 return iterator
{m_data
->iteratorAt(0),
113 deepInitBits().iteratorAt(0)};
116 inline Class::PropInitVec::iterator
Class::PropInitVec::end() {
117 return iterator
{m_data
->iteratorAt(m_size
),
118 deepInitBits().iteratorAt(m_size
)};
121 inline Class::PropInitVec::const_iterator
Class::PropInitVec::cbegin() const {
122 return const_iterator
{
123 const_cast<const ObjectProps
*>(m_data
)->iteratorAt(0),
124 deepInitBits().iteratorAt(0)};
127 inline Class::PropInitVec::const_iterator
Class::PropInitVec::cend() const {
128 return const_iterator
{
129 const_cast<const ObjectProps
*>(m_data
)->iteratorAt(m_size
),
130 deepInitBits().iteratorAt(m_size
)};
133 inline size_t Class::PropInitVec::size() const {
137 inline bool Class::PropInitVec::reqAllocated() const {
138 return m_capacity
< 0;
141 inline const ObjectProps
* Class::PropInitVec::data() const {
145 inline BitsetView
<false> Class::PropInitVec::deepInitBits() {
146 auto const cap
= m_capacity
< 0 ? ~m_capacity
: m_capacity
;
147 return BitsetView
<false>{reinterpret_cast<unsigned char*>(m_data
) +
148 ObjectProps::sizeFor(cap
)};
151 inline BitsetView
<true>
152 Class::PropInitVec::deepInitBits() const {
153 auto const cap
= m_capacity
< 0 ? ~m_capacity
: m_capacity
;
154 return BitsetView
<true>{reinterpret_cast<const unsigned char*>(m_data
) +
155 ObjectProps::sizeFor(cap
)};
159 ///////////////////////////////////////////////////////////////////////////////
160 // Pre- and post-allocations.
162 inline LowPtr
<Func
>* Class::funcVec() const {
163 return reinterpret_cast<LowPtr
<Func
>*>(
164 reinterpret_cast<uintptr_t>(this) -
165 m_funcVecLen
* sizeof(LowPtr
<Func
>)
169 inline void* Class::mallocPtr() const {
170 return reinterpret_cast<void*>(
171 reinterpret_cast<uintptr_t>(funcVec()) & ~(alignof(Class
) - 1)
175 inline const void* Class::mallocEnd() const {
176 return reinterpret_cast<const char*>(this)
177 + Class::classVecOff()
178 + classVecLen() * sizeof(*classVec());
181 inline const LowPtr
<Class
>* Class::classVec() const {
185 inline Class::veclen_t
Class::classVecLen() const {
186 return m_classVecLen
;
189 ///////////////////////////////////////////////////////////////////////////////
192 inline bool Class::classofNonIFace(const Class
* cls
) const {
193 assertx(!(cls
->attrs() & AttrInterface
));
194 if (m_classVecLen
>= cls
->m_classVecLen
) {
195 return (m_classVec
[cls
->m_classVecLen
-1] == cls
);
200 inline bool Class::classof(const Class
* cls
) const {
201 // If `cls' is an interface, we can simply check to see if cls is in
202 // this->m_interfaces. Otherwise, if `this' is not an interface, the
203 // classVec check will determine whether it's an instance of cls (including
204 // the case where this and cls are the same trait). Otherwise, `this' is an
205 // interface, and `cls' is not, so we need to return false. But the classVec
206 // check can never return true in that case (cls's classVec contains only
207 // non-interfaces, while this->classVec is either empty, or contains
209 if (UNLIKELY(cls
->attrs() & AttrInterface
)) {
210 return this == cls
||
211 m_interfaces
.lookupDefault(cls
->m_preClass
->name(), nullptr) == cls
;
213 if (UNLIKELY(cls
->attrs() & this->attrs() & AttrEnum
)) {
216 } else if (cls
->m_extra
&& cls
->m_extra
->m_includedEnums
.size() != 0) {
217 cls
->m_extra
->m_includedEnums
.lookupDefault(this->m_preClass
->name(), nullptr) == this;
220 return classofNonIFace(cls
);
223 inline bool Class::subtypeOf(const Class
* cls
) const { return classof(cls
); }
225 inline bool Class::ifaceofDirect(const StringData
* name
) const {
226 return m_interfaces
.contains(name
);
229 ///////////////////////////////////////////////////////////////////////////////
232 inline const StringData
* Class::name() const {
233 return m_preClass
->name();
236 inline const PreClass
* Class::preClass() const {
237 return m_preClass
.get();
240 inline Class
* Class::parent() const {
241 return m_parent
.get();
244 inline StrNR
Class::nameStr() const {
245 return m_preClass
->nameStr();
248 inline StrNR
Class::parentStr() const {
249 return m_preClass
->parentStr();
252 inline Attr
Class::attrs() const {
253 assertx(Attr(m_attrCopy
) == m_preClass
->attrs());
254 return Attr(m_attrCopy
);
257 inline bool Class::rtAttribute(RuntimeAttribute a
) const {
258 return m_RTAttrs
& a
;
261 inline void Class::initRTAttributes(uint8_t a
) {
265 inline bool Class::isUnique() const {
266 return attrs() & AttrUnique
;
269 inline bool Class::isPersistent() const {
270 return attrs() & AttrPersistent
;
273 inline bool Class::isDynamicallyConstructible() const {
274 return attrs() & AttrDynamicallyConstructible
;
277 inline folly::Optional
<int64_t> Class::dynConstructSampleRate() const {
278 auto const rate
= preClass()->dynConstructSampleRate();
279 if (rate
< 0) return {};
284 ///////////////////////////////////////////////////////////////////////////////
287 inline const Func
* Class::getCtor() const {
291 inline const Func
* Class::getToString() const {
295 inline const Func
* Class::get86pinit() const {
296 return m_pinitVec
.back();
299 inline const Func
* Class::get86sinit() const {
300 return m_sinitVec
.back();
303 inline const Func
* Class::get86linit() const {
304 return m_linitVec
.back();
307 ///////////////////////////////////////////////////////////////////////////////
310 inline bool Class::isBuiltin() const {
311 return attrs() & AttrBuiltin
;
314 template <bool Unlocked
>
315 inline BuiltinCtorFunction
Class::instanceCtor() const {
316 return Unlocked
? m_extra
->m_instanceCtorUnlocked
: m_extra
->m_instanceCtor
;
319 inline BuiltinDtorFunction
Class::instanceDtor() const {
320 return m_extra
->m_instanceDtor
;
323 ///////////////////////////////////////////////////////////////////////////////
326 inline ObjReleaseFunc
Class::releaseFunc() const {
327 return m_releaseFunc
;
330 inline uint32_t Class::memoSize() const {
334 inline uint8_t Class::sizeIdx() const {
338 ///////////////////////////////////////////////////////////////////////////////
341 inline size_t Class::numMethods() const {
342 return m_methods
.size();
345 inline Func
* Class::getMethod(Slot idx
) const {
346 auto funcVec
= (LowPtr
<Func
>*)this;
347 return funcVec
[-((int32_t)idx
+ 1)];
350 inline void Class::setMethod(Slot idx
, Func
* func
) {
351 auto funcVec
= (LowPtr
<Func
>*)this;
352 funcVec
[-((int32_t)idx
+ 1)] = func
;
355 inline Func
* Class::lookupMethod(const StringData
* methName
) const {
356 Slot
* idx
= m_methods
.find(methName
);
357 if (!idx
) return nullptr;
358 return getMethod(*idx
);
361 ///////////////////////////////////////////////////////////////////////////////
362 // Property metadata.
364 inline size_t Class::numDeclProperties() const {
365 return m_declProperties
.size();
368 inline size_t Class::numStaticProperties() const {
369 return m_staticProperties
.size();
372 inline uint32_t Class::declPropNumAccessible() const {
373 return m_declPropNumAccessible
;
376 inline folly::Range
<const Class::Prop
*> Class::declProperties() const {
377 return m_declProperties
.range();
380 inline folly::Range
<const Class::SProp
*> Class::staticProperties() const {
381 return m_staticProperties
.range();
384 inline Slot
Class::lookupDeclProp(const StringData
* propName
) const {
385 return m_declProperties
.findIndex(propName
);
388 inline Slot
Class::lookupSProp(const StringData
* sPropName
) const {
389 return m_staticProperties
.findIndex(sPropName
);
392 inline Slot
Class::lookupReifiedInitProp() const {
393 return m_declProperties
.findIndex(s_86reified_prop
.get());
396 inline bool Class::hasReifiedGenerics() const {
397 return m_hasReifiedGenerics
;
400 inline bool Class::hasReifiedParent() const {
401 return m_hasReifiedParent
;
404 inline RepoAuthType
Class::declPropRepoAuthType(Slot index
) const {
405 return m_declProperties
[index
].repoAuthType
;
408 inline RepoAuthType
Class::staticPropRepoAuthType(Slot index
) const {
409 return m_staticProperties
[index
].repoAuthType
;
412 inline const TypeConstraint
& Class::declPropTypeConstraint(Slot index
) const {
413 return m_declProperties
[index
].typeConstraint
;
416 inline const TypeConstraint
& Class::staticPropTypeConstraint(Slot index
) const {
417 return m_staticProperties
[index
].typeConstraint
;
420 inline bool Class::hasDeepInitProps() const {
421 return m_hasDeepInitProps
;
424 inline bool Class::forbidsDynamicProps() const {
425 return attrs() & AttrForbidDynamicProps
;
428 inline bool Class::serialize() const {
429 if (m_serialized
) return false;
434 inline bool Class::wasSerialized() const {
438 ///////////////////////////////////////////////////////////////////////////////
439 // Property initialization.
441 inline bool Class::needInitialization() const {
442 return m_needInitialization
;
445 inline bool Class::maybeRedefinesPropTypes() const {
446 return m_maybeRedefsPropTy
;
449 inline bool Class::needsPropInitialValueCheck() const {
450 return m_needsPropInitialCheck
;
453 inline const Class::PropInitVec
& Class::declPropInit() const {
454 return m_declPropInit
;
457 inline const VMFixedVector
<const Func
*>& Class::pinitVec() const {
461 inline rds::Handle
Class::checkedPropTypeRedefinesHandle() const {
462 assertx(m_maybeRedefsPropTy
);
463 m_extra
->m_checkedPropTypeRedefs
.bind(
465 rds::LinkName
{"PropTypeRedefs", name()}
467 return m_extra
->m_checkedPropTypeRedefs
.handle();
470 inline rds::Handle
Class::checkedPropInitialValuesHandle() const {
471 assertx(m_needsPropInitialCheck
);
472 m_extra
->m_checkedPropInitialValues
.bind(
474 rds::LinkName
{"PropInitialValues", name()}
476 return m_extra
->m_checkedPropInitialValues
.handle();
479 ///////////////////////////////////////////////////////////////////////////////
482 inline void Class::initPropHandle() const {
483 m_propDataCache
.bind(
485 rds::LinkName
{"PropDataCache", name()}
489 inline rds::Handle
Class::propHandle() const {
490 return m_propDataCache
.handle();
493 inline rds::Handle
Class::sPropInitHandle() const {
494 return m_sPropCacheInit
.handle();
497 inline rds::Handle
Class::sPropHandle(Slot index
) const {
498 return sPropLink(index
).handle();
501 inline rds::Link
<StaticPropData
, rds::Mode::NonNormal
>
502 Class::sPropLink(Slot index
) const {
503 assertx(m_sPropCacheInit
.bound());
504 assertx(numStaticProperties() > index
);
505 return m_sPropCache
[index
];
508 inline rds::Link
<bool, rds::Mode::NonLocal
> Class::sPropInitLink() const {
509 return m_sPropCacheInit
;
512 ///////////////////////////////////////////////////////////////////////////////
515 inline size_t Class::numConstants() const {
516 return m_constants
.size();
519 inline const Class::Const
* Class::constants() const {
520 return m_constants
.accessList();
523 inline bool Class::hasConstant(const StringData
* clsCnsName
) const {
524 // m_constants.contains(clsCnsName) returns abstract constants
525 auto clsCnsInd
= m_constants
.findIndex(clsCnsName
);
526 return (clsCnsInd
!= kInvalidSlot
) &&
527 !m_constants
[clsCnsInd
].isAbstract() &&
528 m_constants
[clsCnsInd
].kind() == ConstModifiers::Kind::Value
;
531 inline bool Class::hasTypeConstant(const StringData
* typeConstName
,
532 bool includeAbs
) const {
533 auto typeConstInd
= m_constants
.findIndex(typeConstName
);
534 return (typeConstInd
!= kInvalidSlot
) &&
535 (!m_constants
[typeConstInd
].isAbstract() || includeAbs
) &&
536 m_constants
[typeConstInd
].kind() == ConstModifiers::Kind::Type
;
539 ///////////////////////////////////////////////////////////////////////////////
540 // Interfaces and traits.
542 inline folly::Range
<const ClassPtr
*> Class::declInterfaces() const {
543 return folly::range(m_declInterfaces
.begin(),
544 m_declInterfaces
.end());
547 inline const Class::InterfaceMap
& Class::allInterfaces() const {
551 inline const bool Class::hasIncludedEnums() const {
552 return m_extra
&& (m_extra
->m_includedEnums
.size() != 0);
555 inline const Class::IncludedEnumMap
& Class::allIncludedEnums() const {
556 return m_extra
->m_includedEnums
;
559 inline Slot
Class::traitsBeginIdx() const {
560 return m_extra
->m_traitsBeginIdx
;
563 inline Slot
Class::traitsEndIdx() const {
564 return m_extra
->m_traitsEndIdx
;
567 inline const VMCompactVector
<ClassPtr
>& Class::usedTraitClasses() const {
568 return m_extra
->m_usedTraits
;
571 inline const Class::TraitAliasVec
& Class::traitAliases() const {
572 return m_extra
->m_traitAliases
;
575 inline void Class::addTraitAlias(const PreClass::TraitAliasRule
& rule
) const {
577 m_extra
.raw()->m_traitAliases
.push_back(rule
.asNamePair());
580 inline const Class::RequirementMap
& Class::allRequirements() const {
581 return m_requirements
;
584 ///////////////////////////////////////////////////////////////////////////////
587 inline bool Class::checkInstanceBit(unsigned int bit
) const {
589 return m_instanceBits
[bit
];
592 ///////////////////////////////////////////////////////////////////////////////
593 // Throwable initialization.
595 inline bool Class::needsInitThrowable() const {
596 return m_needsInitThrowable
;
599 ///////////////////////////////////////////////////////////////////////////////
602 inline rds::Handle
Class::classHandle() const {
603 return m_cachedClass
.handle();
606 inline void Class::setClassHandle(rds::Link
<LowPtr
<Class
>,
607 rds::Mode::NonLocal
> link
) const {
608 assertx(!m_cachedClass
.bound());
609 m_cachedClass
= link
;
612 inline Class
* Class::getCached() const {
613 return m_cachedClass
.isInit() ? *m_cachedClass
: nullptr;
616 inline void Class::setCached() {
617 m_cachedClass
.initWith(this);
620 ///////////////////////////////////////////////////////////////////////////////
623 inline const Native::NativeDataInfo
* Class::getNativeDataInfo() const {
624 return m_extra
->m_nativeDataInfo
;
627 ///////////////////////////////////////////////////////////////////////////////
628 // Closure subclasses.
630 inline bool Class::isScopedClosure() const {
634 inline const Class::ScopedClonesMap
& Class::scopedClones() const {
635 return m_extra
->m_scopedClones
;
638 /////////////////////////////////////////////////////////////////////////////
641 inline size_t Class::numMemoSlots() const {
642 return m_extra
->m_nextMemoSlot
;
645 inline bool Class::hasMemoSlots() const {
646 return numMemoSlots() > 0;
649 inline std::pair
<Slot
, bool> Class::memoSlotForFunc(FuncId func
) const {
650 assertx(hasMemoSlots());
651 auto const it
= m_extra
->m_memoMappings
.find(func
);
652 if (it
!= m_extra
->m_memoMappings
.end()) return it
->second
;
653 // Each mapping is only stored in the class which defines it, so recurse up to
654 // the parent. We should only be calling this with functions which have a memo
655 // slot, so assert if we reach the end without finding a slot.
656 if (m_parent
) return m_parent
->memoSlotForFunc(func
);
657 always_assert(false);
660 ///////////////////////////////////////////////////////////////////////////////
663 inline MaybeDataType
Class::enumBaseTy() const {
667 inline EnumValues
* Class::getEnumValues() const {
668 return m_extra
->m_enumValues
.load(std::memory_order_relaxed
);
671 ///////////////////////////////////////////////////////////////////////////////
674 inline void Class::allocExtraData() const {
676 m_extra
= new ExtraData();
680 ///////////////////////////////////////////////////////////////////////////////
681 // Non-member functions.
683 inline Attr
classKindAsAttr(ClassKind kind
) {
684 return static_cast<Attr
>(kind
);
687 inline bool isTrait(const Class
* cls
) {
688 return cls
->attrs() & AttrTrait
;
691 inline bool isEnum(const Class
* cls
) {
692 return cls
->attrs() & AttrEnum
;
695 inline bool isInterface(const Class
* cls
) {
696 return cls
->attrs() & AttrInterface
;
699 inline bool isNormalClass(const Class
* cls
) {
700 return !(cls
->attrs() & (AttrTrait
| AttrInterface
| AttrEnum
));
703 inline bool isAbstract(const Class
* cls
) {
704 return cls
->attrs() & AttrAbstract
;
707 inline bool classHasPersistentRDS(const Class
* cls
) {
708 return cls
!= nullptr &&
709 rds::isPersistentHandle(cls
->classHandle());
712 inline const StringData
* classToStringHelper(const Class
* cls
) {
713 if (RuntimeOption::EvalRaiseClassConversionWarning
) {
714 raise_warning(Strings::CLASS_TO_STRING
);
719 ///////////////////////////////////////////////////////////////////////////////
722 inline Class
* Class::lookup(const NamedEntity
* ne
) {
723 return ne
->getCachedClass();
726 inline Class
* Class::lookup(const StringData
* name
) {
727 return lookup(NamedEntity::get(name
));
730 inline const Class
* Class::lookupUniqueInContext(const NamedEntity
* ne
,
733 Class
* cls
= ne
->clsList();
734 if (UNLIKELY(cls
== nullptr)) return nullptr;
735 if (cls
->attrs() & AttrUnique
) return cls
;
736 if (unit
&& cls
->preClass()->unit() == unit
) return cls
;
737 if (!ctx
) return nullptr;
738 return ctx
->getClassDependency(cls
->name());
741 inline const Class
* Class::lookupUniqueInContext(const StringData
* name
,
744 return lookupUniqueInContext(NamedEntity::get(name
), ctx
, unit
);
747 inline Class
* Class::load(const StringData
* name
) {
748 if (name
->isSymbol()) {
749 if (auto const result
= name
->getCachedClass()) return result
;
751 auto const orig
= name
;
753 auto const result
= [&]() -> Class
* {
755 auto ne
= NamedEntity::get(name
, true, &normStr
);
757 // Try to fetch from cache
758 Class
* class_
= ne
->getCachedClass();
759 if (LIKELY(class_
!= nullptr)) return class_
;
761 // Normalize the namespace
762 if (normStr
) name
= normStr
.get();
764 // Autoload the class
765 return load(ne
, name
);
768 if (orig
->isSymbol() && result
&& classHasPersistentRDS(result
)) {
769 const_cast<StringData
*>(orig
)->setCachedClass(result
);
774 inline Class
* Class::get(const StringData
* name
, bool tryAutoload
) {
776 auto ne
= NamedEntity::get(name
, true, &normStr
);
778 name
= normStr
.get();
780 return get(ne
, name
, tryAutoload
);
783 ///////////////////////////////////////////////////////////////////////////////