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 #include "hphp/runtime/base/array-init.h"
18 #include "hphp/runtime/base/collections.h"
19 #include "hphp/runtime/base/comparisons.h"
20 #include "hphp/runtime/base/enum-cache.h"
21 #include "hphp/runtime/base/mixed-array.h"
22 #include "hphp/runtime/base/rds.h"
23 #include "hphp/runtime/base/strings.h"
24 #include "hphp/runtime/base/tv-refcount.h"
25 #include "hphp/runtime/base/type-structure.h"
26 #include "hphp/runtime/base/typed-value.h"
27 #include "hphp/runtime/server/memory-stats.h"
28 #include "hphp/runtime/vm/jit/irgen-minstr.h"
29 #include "hphp/runtime/vm/jit/translator.h"
30 #include "hphp/runtime/vm/instance-bits.h"
31 #include "hphp/runtime/vm/memo-cache.h"
32 #include "hphp/runtime/vm/native-data.h"
33 #include "hphp/runtime/vm/native-prop-handler.h"
34 #include "hphp/runtime/vm/property-profile.h"
35 #include "hphp/runtime/vm/reified-generics.h"
36 #include "hphp/runtime/vm/trait-method-import-data.h"
37 #include "hphp/runtime/vm/treadmill.h"
38 #include "hphp/runtime/vm/unit-util.h"
39 #include "hphp/runtime/vm/vm-regs.h"
41 #include "hphp/runtime/ext/collections/ext_collections.h"
42 #include "hphp/runtime/ext/string/ext_string.h"
43 #include "hphp/runtime/ext/std/ext_std_closure.h"
45 #include "hphp/util/logger.h"
47 #include <folly/Bits.h>
48 #include <folly/MapUtil.h>
49 #include <folly/Optional.h>
54 TRACE_SET_MOD(class_load
);
57 ///////////////////////////////////////////////////////////////////////////////
59 const StaticString
s_86cinit("86cinit");
60 const StaticString
s_86pinit("86pinit");
61 const StaticString
s_86sinit("86sinit");
62 const StaticString
s_86linit("86linit");
63 const StaticString
s_86ctor("86ctor");
64 const StaticString
s_86metadata("86metadata");
65 const StaticString
s_86reified_prop("86reified_prop");
66 const StaticString
s_86reifiedinit("86reifiedinit");
67 const StaticString
s___MockClass("__MockClass");
68 const StaticString
s___Reified("__Reified");
72 ///////////////////////////////////////////////////////////////////////////////
73 // Class::PropInitVec.
75 Class::PropInitVec::~PropInitVec() {
77 // allocated in low heap
83 Class::PropInitVec::allocWithReqAllocator(const PropInitVec
& src
) {
84 uint32_t size
= src
.m_size
;
85 auto const props_size
= ObjectProps::sizeFor(size
);
86 auto const bits_size
= BitsetView
<true>::sizeFor(size
);
88 PropInitVec
* p
= reinterpret_cast<PropInitVec
*>(req::malloc(
89 sizeof(PropInitVec
) + props_size
+ bits_size
,
90 type_scan::getIndexForMalloc
<
92 type_scan::Action::WithSuffix
<char>
97 p
->m_capacity
= ~size
;
98 p
->m_data
= reinterpret_cast<ObjectProps
*>(p
+ 1);
100 // these are two distinct memcpys because the props and deep init bits
101 // aren't necessarily contiguous in src (because capacity >= size)
102 // but will be in the destination (which is exactly sized)
103 memcpy16_inline(p
->m_data
, src
.m_data
, props_size
);
104 memcpy(reinterpret_cast<char*>(p
->m_data
) +
105 ObjectProps::sizeFor(size
),
106 src
.deepInitBits().buffer(),
112 const Class::PropInitVec
&
113 Class::PropInitVec::operator=(const PropInitVec
& piv
) {
114 assertx(!reqAllocated());
116 if (UNLIKELY(m_capacity
)) {
120 m_size
= m_capacity
= piv
.size();
121 auto const props_size
= ObjectProps::sizeFor(m_size
);
122 auto const bits_size
= BitsetView
<true>::sizeFor(m_size
);
123 if (m_size
== 0) return *this;
125 m_data
= reinterpret_cast<ObjectProps
*>(
126 lower_malloc(props_size
+ bits_size
)
128 // these are two distinct memcpys because the props and deep init bits
129 // aren't necessarily contiguous in src (because capacity >= size)
130 // but will be in the destination (which is exactly sized)
131 memcpy16_inline(m_data
, piv
.m_data
, props_size
);
132 memcpy(deepInitBits().buffer(),
133 piv
.deepInitBits().buffer(),
137 assertx(m_data
->checkInvariants(m_capacity
));
142 void Class::PropInitVec::push_back(const TypedValue
& v
) {
143 assertx(!reqAllocated());
144 if (m_size
== m_capacity
) {
145 unsigned newCap
= folly::nextPowTwo(m_size
+ 1);
146 auto const props_size
= ObjectProps::sizeFor(newCap
);
147 auto const bits_size
= BitsetView
<true>::sizeFor(newCap
);
149 auto newData
= reinterpret_cast<char*>(
150 lower_malloc(props_size
+ bits_size
)
153 auto const oldSize
= ObjectProps::sizeFor(m_size
);
155 // TODO(jgriego) this is a kludge to preserve invariants of
156 // ObjectProps (when using 7up layouts) and should be replaced
157 // with some kind of `grow` fn on the layout class
158 memset(newData
+ oldSize
, 0, props_size
- oldSize
);
161 // these two memcpys are separate because we're going from
162 // contiguous memory (since the size == capacity) to noncontiguous memory
163 // (because the structure has grown)
164 memcpy16_inline(newData
, m_data
, oldSize
);
165 memcpy(newData
+ props_size
,
166 deepInitBits().buffer(),
171 m_data
= reinterpret_cast<ObjectProps
*>(newData
);
172 m_capacity
= static_cast<int32_t>(newCap
);
175 assertx(m_data
->checkInvariants(m_capacity
));
177 auto const idx
= m_size
++;
178 tvDup(v
, m_data
->at(idx
));
179 deepInitBits()[idx
] = false;
183 ///////////////////////////////////////////////////////////////////////////////
189 * Load used traits of PreClass `preClass', and append the trait Class*'s to
190 * 'usedTraits'. Return an estimate of the method count of all used traits.
192 unsigned loadUsedTraits(PreClass
* preClass
,
193 VMCompactVector
<ClassPtr
>& usedTraits
) {
194 unsigned methodCount
= 0;
196 auto const traitsFlattened
= !!(preClass
->attrs() & AttrNoExpandTrait
);
197 for (auto const& traitName
: preClass
->usedTraits()) {
198 Class
* classPtr
= Unit::loadClass(traitName
);
199 if (classPtr
== nullptr) {
200 raise_error(Strings::TRAITS_UNKNOWN_TRAIT
, traitName
->data());
202 if (!(classPtr
->attrs() & AttrTrait
)) {
203 raise_error("%s cannot use %s - it is not a trait",
204 preClass
->name()->data(),
205 classPtr
->name()->data());
208 preClass
->enforceInMaybeSealedParentWhitelist(classPtr
->preClass());
209 if (traitsFlattened
) {
210 // In RepoAuthoritative mode (with the WholeProgram compiler
211 // optimizations), the contents of traits can be flattened into
212 // the preClasses of "use"r classes. Continuing here allows us
213 // to avoid unnecessarily attempting to re-import trait methods
214 // and properties, only to fail due to (surprise surprise!) the
215 // same method/property existing on m_preClass.
219 usedTraits
.push_back(ClassPtr(classPtr
));
220 methodCount
+= classPtr
->numMethods();
224 if (!traitsFlattened
) {
225 // Trait aliases can increase method count. Get an estimate of the
226 // number of aliased functions. This doesn't need to be done in
227 // classes with flattened traits because those methods are already
228 // present in the preclass.
229 for (auto const& rule
: preClass
->traitAliasRules()) {
230 auto origName
= rule
.origMethodName();
231 auto newName
= rule
.newMethodName();
232 if (origName
!= newName
) {
241 * Class ends with a dynamically sized array, m_classVec. C++ doesn't allow
242 * declaring empty arrays like C does, so we give it size 1 and use
243 * m_classVec's offset as the true size of Class when allocating memory to
246 constexpr size_t sizeof_Class
= Class::classVecOff();
249 struct assert_sizeof_class
{
250 // If this static_assert fails, the compiler error will have the real value
251 // of sizeof_Class in it since it's in this struct's type.
253 static_assert(sz
== (use_lowptr
? 276 : 328), "Change this only on purpose");
255 static_assert(sz
== (use_lowptr
? 272 : 320), "Change this only on purpose");
258 template struct assert_sizeof_class
<sizeof_Class
>;
261 * R/W lock for caching scopings of closures.
263 ReadWriteMutex s_scope_cache_mutex
;
267 Class
* Class::newClass(PreClass
* preClass
, Class
* parent
) {
268 auto const classVecLen
= parent
!= nullptr ? parent
->m_classVecLen
+ 1 : 1;
269 auto funcVecLen
= (parent
!= nullptr ? parent
->m_methods
.size() : 0)
270 + preClass
->numMethods();
271 if (parent
== nullptr && !(preClass
->attrs() & AttrNoReifiedInit
)) {
272 // We will need to add 86reifiedinit method
276 VMCompactVector
<ClassPtr
> usedTraits
;
277 auto numTraitMethodsEstimate
= loadUsedTraits(preClass
, usedTraits
);
278 funcVecLen
+= numTraitMethodsEstimate
;
280 // We need to pad this allocation so that the actual start of the Class is
282 auto const mask
= alignof(Class
) - 1;
283 auto const funcvec_sz
= sizeof(LowPtr
<Func
>) * funcVecLen
;
284 auto const prefix_sz
= (funcvec_sz
+ mask
) & ~mask
;
286 auto const size
= sizeof_Class
+ prefix_sz
287 + sizeof(m_classVec
[0]) * classVecLen
;
289 auto const mem
= lower_malloc(size
);
290 MemoryStats::LogAlloc(AllocKind::Class
, size
);
291 auto const classPtr
= reinterpret_cast<void*>(
292 reinterpret_cast<uintptr_t>(mem
) + prefix_sz
295 return new (classPtr
) Class(preClass
, parent
, std::move(usedTraits
),
296 classVecLen
, funcVecLen
);
303 Class
* Class::rescope(Class
* ctx
) {
304 assertx(parent() == c_Closure::classof());
307 // Look up the generated template class for this particular subclass of
308 // Closure. This class maintains the table of scoped clones of itself, and
309 // if we create a new scoped clone, we need to map it there.
310 auto template_cls
= this;
311 auto const invoke
= template_cls
->m_invoke
;
313 auto const try_template
= [&]() -> Class
* {
314 if (invoke
->cls() != ctx
) return nullptr;
316 // The first scoping will never be for `template_cls' (since it's
317 // impossible to define a closure in the context of its own Closure
318 // subclass), so if this happens, it means that `template_cls' is in a
319 // "de-scoped" state and we shouldn't use it. (See Class::releaseRefs().)
320 if (template_cls
->m_scoped
&& invoke
->cls() == template_cls
) {
326 // If the template class has already been scoped to `ctx', we're done. This
327 // is the common case in repo mode.
328 if (auto cls
= try_template()) return cls
;
330 template_cls
->allocExtraData();
331 auto& scopedClones
= template_cls
->m_extra
.raw()->m_scopedClones
;
333 auto const key
= ctx
;
335 auto const try_cache
= [&] {
336 auto it
= scopedClones
.find(key
);
337 return it
!= scopedClones
.end() ? it
->second
.get() : nullptr;
340 { // Return the cached clone if we have one.
341 ReadLock
l(s_scope_cache_mutex
);
343 // If this succeeds, someone raced us to scoping the template. We may have
344 // unnecessarily allocated an ExtraData, but whatever.
345 if (auto cls
= try_template()) return cls
;
347 if (auto cls
= try_cache()) return cls
;
350 auto cloneClass
= [&] {
351 auto const cls
= newClass(m_preClass
.get(), m_parent
.get());
355 // We use the French for closure because using the English crashes gcc in the
356 // implicit lambda capture below. (This is fixed in gcc 4.8.5.)
357 auto fermeture
= ClassPtr
{
358 template_cls
->m_scoped
? cloneClass() : template_cls
361 WriteLock
l(s_scope_cache_mutex
);
363 if (fermeture
->m_scoped
) {
364 // We raced with someone who scoped the template_cls just as we were about
365 // to, so make a new Class. (Note that we don't want to do this with the
366 // lock held, since it's very expensive.)
368 // This race should be far less likely than a race between two attempted
369 // first-scopings for `template_cls', which is why we don't do an test-and-
370 // set when we first check `m_scoped' before acquiring the lock.
371 s_scope_cache_mutex
.release();
372 SCOPE_EXIT
{ s_scope_cache_mutex
.acquireWrite(); };
373 fermeture
= ClassPtr
{ cloneClass() };
376 // Check the caches again.
377 if (auto cls
= try_template()) return cls
;
378 if (auto cls
= try_cache()) return cls
;
380 fermeture
->m_invoke
->rescope(ctx
);
381 fermeture
->m_invoke
->setHasForeignThis(false);
382 fermeture
->m_scoped
= true;
384 if (ctx
!= nullptr &&
385 !RuntimeOption::RepoAuthoritative
&&
386 !classHasPersistentRDS(ctx
)) {
387 // If the context Class might be destroyed, we need to do extra accounting
388 // so that we can drop all clones scoped to it at the time of destruction.
389 ctx
->allocExtraData();
390 ctx
->m_extra
.raw()->m_clonesWithThisScope
.push_back(
391 ClassPtr(template_cls
));
394 auto updateClones
= [&] {
395 if (template_cls
!= fermeture
.get()) {
396 scopedClones
[key
] = fermeture
;
397 fermeture
.get()->setClassHandle(template_cls
->m_cachedClass
);
401 InstanceBits::ifInitElse(
403 fermeture
->setInstanceBits();
411 return fermeture
.get();
414 EnumValues
* Class::setEnumValues(EnumValues
* values
) {
415 auto extra
= m_extra
.ensureAllocated();
416 EnumValues
* expected
= nullptr;
417 if (!extra
->m_enumValues
.compare_exchange_strong(
418 expected
, values
, std::memory_order_relaxed
)) {
419 // Already set by someone else, use theirs.
427 Class::ExtraData::~ExtraData() {
428 delete m_enumValues
.load(std::memory_order_relaxed
);
429 if (m_lsbMemoExtra
.m_handles
) {
430 vm_free(m_lsbMemoExtra
.m_handles
);
434 void Class::destroy() {
436 * If we were never put on NamedEntity::classList, or
437 * we've already been destroy'd, there's nothing to do
439 if (!m_cachedClass
.bound()) return;
441 Lock
l(g_classesMutex
);
442 // Need to recheck now we have the lock
443 if (!m_cachedClass
.bound()) return;
444 // Only do this once.
445 m_cachedClass
= rds::Link
<LowPtr
<Class
>, rds::Mode::NonLocal
>{};
448 * Regardless of refCount, this Class is now unusable. Remove it
449 * from the class list.
451 * Needs to be under the lock, because multiple threads could call
452 * destroy, or want to manipulate the class list. (It's safe for
453 * other threads to concurrently read the class list without the
456 auto const pcls
= m_preClass
.get();
457 pcls
->namedEntity()->removeClass(this);
460 // Other threads find this class via rds::s_handleTable.
461 // Remove our sprop entries before the treadmill delay.
462 for (Slot i
= 0, n
= numStaticProperties(); i
< n
; ++i
) {
463 if (m_staticProperties
[i
].cls
== this) {
464 auto const &link
= m_sPropCache
[i
];
466 rds::unbind(rds::SPropCache
{this, i
}, link
.handle());
475 if (!this->decAtomicCount()) this->atomicRelease();
480 void Class::atomicRelease() {
481 assertx(!m_cachedClass
.bound());
482 assertx(!getCount());
484 lower_free(mallocPtr());
488 releaseRefs(); // must be called for Func-nulling side effects
491 for (unsigned i
= 0, n
= numStaticProperties(); i
< n
; ++i
) {
492 m_sPropCache
[i
].~Link();
494 vm_sized_free(m_sPropCache
, numStaticProperties() * sizeof(*m_sPropCache
));
497 for (auto i
= size_t{}, n
= numMethods(); i
< n
; i
++) {
498 if (auto meth
= getMethod(i
)) {
499 if (meth
->isPreFunc()) {
508 delete m_extra
.raw();
512 EnumCache::deleteValues(this);
514 if (auto p
= m_vtableVec
.get()) {
524 void Class::releaseRefs() {
526 * We have to be careful here.
527 * We want to free up as much as possible as early as possible, but
528 * some of our methods may actually belong to our parent
529 * This means we can't destroy *our* Funcs until our refCount
530 * hits zero (ie when Class::~Class gets called), because there
531 * could be a child class which hasn't yet been destroyed, which
532 * will need to inspect them. Also, we need to inspect the Funcs
533 * now (while we still have a references to parent) to determine
534 * which ones we will eventually need to free.
535 * Similarly, if any of our func's belong to a parent class, we
536 * can't free the parent, because one of our children could also
537 * have a reference to those func's (and its only reference to
538 * our parent is via this class).
540 auto num
= numMethods();
541 bool okToReleaseParent
= true;
542 for (auto i
= 0; i
< num
; i
++) {
543 Func
* meth
= getMethod(i
);
544 if (meth
/* releaseRefs can be called more than once */ &&
545 meth
->cls() != this) {
546 setMethod(i
, nullptr);
547 okToReleaseParent
= false;
551 if (okToReleaseParent
) {
555 m_declInterfaces
.clear();
556 m_requirements
.clear();
559 auto xtra
= m_extra
.raw();
560 xtra
->m_usedTraits
.clear();
562 if (xtra
->m_clonesWithThisScope
.size() > 0) {
563 WriteLock
l(s_scope_cache_mutex
);
565 // Purge all references to scoped closure clones that are scoped to
566 // `this'---there is no way anyone can find them at this point.
567 for (auto const template_cls
: xtra
->m_clonesWithThisScope
) {
568 auto const invoke
= template_cls
->m_invoke
;
570 if (invoke
->cls() == this) {
571 // We only hijack the `template_cls' as a clone for static rescopings
572 // (which were signified by CloneAttr::None). To undo this, we need
573 // to make sure that /no/ scoping will match with that of
574 // `template_cls'. We can accomplish this by using `template_cls'
575 // itself as the context.
576 // Any instance of this closure will have its own scoped clone, and
577 // we are de-scoping `template_cls' here, so the only way to obtain
578 // it for dynamic binding is to reference it by name, which is
579 // logically private (in PHP7, it's always just "Closure"). In HHVM,
580 // this is possible by obtaining the Closure subclass's name, in
581 // which case we'll just force a fresh clone via a special-case check
582 // in Class::rescope().
583 invoke
->rescope(template_cls
.get());
584 // We explicitly decline to reset template_cls->m_scoped. This lets
585 // us simplify some assertions in rescope(), gives us a nice sanity
586 // check for debugging, and avoids having to play around too much
587 // with how Func::rescope() works, at the cost of preventing the
588 // template from being scoped again. This should only happen outside
589 // of RepoAuthoritative mode while code is being modified, so the
590 // extra memory usage is not a substantial concern.
592 assertx(template_cls
->m_extra
);
593 auto& scopedClones
= template_cls
->m_extra
.raw()->m_scopedClones
;
595 auto const it
= scopedClones
.find(this);
596 assertx(it
!= scopedClones
.end());
597 it
->second
->m_cachedClass
=
598 rds::Link
<LowPtr
<Class
>, rds::Mode::NonLocal
>{};
599 scopedClones
.erase(it
);
603 xtra
->m_clonesWithThisScope
.clear();
607 Class::Avail
Class::avail(Class
*& parent
,
608 bool tryAutoload
/* = false */) const {
609 if (Class
*ourParent
= m_parent
.get()) {
611 PreClass
*ppcls
= ourParent
->m_preClass
.get();
612 parent
= Unit::getClass(ppcls
->namedEntity(),
613 m_preClass
.get()->parent(), tryAutoload
);
619 if (parent
!= ourParent
) {
620 if (UNLIKELY(ourParent
->isZombie())) {
621 const_cast<Class
*>(this)->destroy();
627 for (size_t i
= 0; i
< m_declInterfaces
.size(); i
++) {
628 auto di
= m_declInterfaces
[i
].get();
629 const StringData
* pdi
= m_preClass
.get()->interfaces()[i
];
630 assertx(pdi
->isame(di
->name()));
632 PreClass
*pint
= di
->m_preClass
.get();
633 Class
* interface
= Unit::getClass(pint
->namedEntity(), pdi
,
635 if (interface
!= di
) {
636 if (interface
== nullptr) {
640 if (UNLIKELY(di
->isZombie())) {
641 const_cast<Class
*>(this)->destroy();
647 if (RuntimeOption::RepoAuthoritative
) {
648 if (m_preClass
->usedTraits().size()) {
649 int numIfaces
= m_interfaces
.size();
650 for (int i
= 0; i
< numIfaces
; i
++) {
651 auto di
= m_interfaces
[i
].get();
653 PreClass
*pint
= di
->m_preClass
.get();
654 Class
* interface
= Unit::getClass(pint
->namedEntity(), pint
->name(),
656 if (interface
!= di
) {
657 if (interface
== nullptr) {
661 if (UNLIKELY(di
->isZombie())) {
662 const_cast<Class
*>(this)->destroy();
669 for (size_t i
= 0, n
= m_extra
->m_usedTraits
.size(); i
< n
; ++i
) {
670 auto usedTrait
= m_extra
->m_usedTraits
[i
].get();
671 const StringData
* usedTraitName
= m_preClass
.get()->usedTraits()[i
];
672 PreClass
* ptrait
= usedTrait
->m_preClass
.get();
673 Class
* trait
= Unit::getClass(ptrait
->namedEntity(), usedTraitName
,
675 if (trait
!= usedTrait
) {
676 if (trait
== nullptr) {
680 if (UNLIKELY(usedTrait
->isZombie())) {
681 const_cast<Class
*>(this)->destroy();
692 ///////////////////////////////////////////////////////////////////////////////
695 const Class
* Class::commonAncestor(const Class
* cls
) const {
696 assertx(isNormalClass(this) && isNormalClass(cls
));
698 // Walk up m_classVec for both classes to look for a common ancestor.
699 auto vecIdx
= std::min(m_classVecLen
, cls
->m_classVecLen
) - 1;
701 assertx(vecIdx
< m_classVecLen
&& vecIdx
< cls
->m_classVecLen
);
702 if (m_classVec
[vecIdx
] == cls
->m_classVec
[vecIdx
]) {
703 return m_classVec
[vecIdx
];
710 const Class
* Class::getClassDependency(const StringData
* name
) const {
711 for (auto idx
= m_classVecLen
; idx
--; ) {
712 auto cls
= m_classVec
[idx
];
713 if (cls
->name()->isame(name
)) return cls
;
716 return m_interfaces
.lookupDefault(name
, nullptr);
719 ///////////////////////////////////////////////////////////////////////////////
722 const Func
* Class::getDeclaredCtor() const {
723 const Func
* f
= getCtor();
724 return f
!= SystemLib::s_nullCtor
? f
: nullptr;
727 const Func
* Class::getCachedInvoke() const {
728 assertx(IMPLIES(m_invoke
, !m_invoke
->isStaticInPrologue()));
732 ///////////////////////////////////////////////////////////////////////////////
735 bool Class::isCppSerializable() const {
736 assertx(instanceCtor()); // Only call this on CPP classes
737 auto* ndi
= m_extra
? m_extra
.raw()->m_nativeDataInfo
: nullptr;
738 return ndi
!= nullptr && ndi
->isSerializable();
741 bool Class::isCollectionClass() const {
743 return collections::isTypeName(s
);
747 ///////////////////////////////////////////////////////////////////////////////
748 // Property initialization.
750 void Class::initialize() const {
751 if (m_maybeRedefsPropTy
) checkPropTypeRedefinitions();
752 if (m_needsPropInitialCheck
) checkPropInitialValues();
754 if (m_pinitVec
.size() > 0 && getPropData() == nullptr) {
757 if (numStaticProperties() > 0 && needsInitSProps()) {
762 bool Class::initialized() const {
763 if (m_pinitVec
.size() > 0 && getPropData() == nullptr) {
766 if (numStaticProperties() > 0 && needsInitSProps()) {
769 if (m_maybeRedefsPropTy
&&
770 (!m_extra
->m_checkedPropTypeRedefs
.bound() ||
771 !m_extra
->m_checkedPropTypeRedefs
.isInit())) {
774 if (m_needsPropInitialCheck
&&
775 (!m_extra
->m_checkedPropInitialValues
.bound() ||
776 !m_extra
->m_checkedPropInitialValues
.isInit())) {
782 void Class::initProps() const {
783 assertx(m_pinitVec
.size() > 0);
784 assertx(getPropData() == nullptr);
785 // Copy initial values for properties to a new vector that can be used to
786 // complete initialization for non-scalar properties via the iterative
787 // 86pinit() calls below. 86pinit() takes a reference to an array to populate
788 // with initial property values; after it completes, we copy the values into
790 auto propVec
= PropInitVec::allocWithReqAllocator(m_declPropInit
);
795 m_propDataCache
.initWith(propVec
);
798 // Iteratively invoke 86pinit() methods upward
799 // through the inheritance chain.
800 for (auto it
= m_pinitVec
.rbegin(); it
!= m_pinitVec
.rend(); ++it
) {
801 DEBUG_ONLY
auto retval
= g_context
->invokeFunc(
802 *it
, init_null_variant
, nullptr, const_cast<Class
*>(this), false
804 assertx(retval
.m_type
== KindOfNull
);
807 // Undo the allocation of propVec
808 req::destroy_raw(propVec
);
809 *m_propDataCache
= nullptr;
810 m_propDataCache
.markUninit();
814 // For properties that do not require deep initialization, promote strings
815 // and arrays that came from 86pinit to static. This allows us to initialize
816 // object properties very quickly because we can just memcpy and we don't
817 // have to do any refcounting.
818 // For properties that require "deep" initialization, we have to do a little
819 // more work at object creation time.
820 for (Slot slot
= 0; slot
< propVec
->size(); slot
++) {
821 auto index
= propSlotToIndex(slot
);
822 auto piv_entry
= (*propVec
)[index
];
823 assertx(!piv_entry
.deepInit
);
824 // Set deepInit if the property requires "deep" initialization.
825 if (m_declProperties
[slot
].attrs
& AttrDeepInit
) {
826 piv_entry
.deepInit
= true;
828 TypedValue tv
= piv_entry
.val
.tv();
829 tvAsVariant(&tv
).setEvalScalar();
830 tvCopy(tv
, piv_entry
.val
);
835 bool Class::needsInitSProps() const {
836 return !m_sPropCacheInit
.bound() || !m_sPropCacheInit
.isInit();
839 void Class::initSProps() const {
840 assertx(needsInitSProps() || m_sPropCacheInit
.isPersistent());
842 const bool hasNonscalarInit
= !m_sinitVec
.empty() || !m_linitVec
.empty();
843 folly::Optional
<VMRegAnchor
> _
;
844 if (hasNonscalarInit
) {
848 // Initialize static props for parent.
849 Class
* parent
= this->parent();
850 if (parent
&& parent
->needsInitSProps()) {
851 parent
->initSProps();
854 if (!numStaticProperties()) return;
858 // Perform scalar inits.
859 for (Slot slot
= 0, n
= m_staticProperties
.size(); slot
< n
; ++slot
) {
860 auto const& sProp
= m_staticProperties
[slot
];
861 // TODO(T61738946): We can remove the temporary here once we no longer
862 // coerce class_meth types.
863 auto val
= sProp
.val
;
865 if ((sProp
.cls
== this && !m_sPropCache
[slot
].isPersistent()) ||
866 sProp
.attrs
& AttrLSB
) {
867 if (RuntimeOption::EvalCheckPropTypeHints
> 0 &&
868 !(sProp
.attrs
& (AttrInitialSatisfiesTC
|AttrSystemInitialValue
)) &&
869 sProp
.val
.m_type
!= KindOfUninit
) {
870 if (sProp
.typeConstraint
.isCheckable()) {
871 sProp
.typeConstraint
.verifyStaticProperty(
878 if (RuntimeOption::EvalEnforceGenericsUB
> 0) {
879 for (auto const& ub
: sProp
.ubs
) {
880 if (ub
.isCheckable()) {
881 ub
.verifyStaticProperty(&val
, this, sProp
.cls
, sProp
.name
);
886 m_sPropCache
[slot
]->val
= val
;
890 // If there are non-scalar initializers (i.e. 86sinit or 86linit methods),
892 // They will override the KindOfUninit values set by scalar initialization.
893 if (hasNonscalarInit
) {
894 for (unsigned i
= 0, n
= m_sinitVec
.size(); i
< n
; i
++) {
895 DEBUG_ONLY
auto retval
= g_context
->invokeFunc(
896 m_sinitVec
[i
], init_null_variant
, nullptr, const_cast<Class
*>(this),
899 assertx(retval
.m_type
== KindOfNull
);
901 for (unsigned i
= 0, n
= m_linitVec
.size(); i
< n
; i
++) {
902 DEBUG_ONLY
auto retval
= g_context
->invokeFunc(
903 m_linitVec
[i
], init_null_variant
, nullptr, const_cast<Class
*>(this),
906 assertx(retval
.m_type
== KindOfNull
);
910 m_sPropCacheInit
.initWith(true);
913 Slot
Class::lsbMemoSlot(const Func
* func
, bool forValue
) const {
916 assertx(func
->numParams() == 0);
918 assertx(func
->numParams() > 0);
920 const auto& slots
= m_extra
->m_lsbMemoExtra
.m_slots
;
921 auto it
= slots
.find(func
->getFuncId());
922 always_assert(it
!= slots
.end());
926 void Class::checkPropInitialValues() const {
927 assertx(m_needsPropInitialCheck
);
928 assertx(RuntimeOption::EvalCheckPropTypeHints
> 0);
929 assertx(m_extra
.get() != nullptr);
931 auto extra
= m_extra
.get();
932 checkedPropInitialValuesHandle(); // init handle
933 if (extra
->m_checkedPropInitialValues
.isInit()) return;
935 for (Slot slot
= 0; slot
< m_declProperties
.size(); ++slot
) {
936 auto const& prop
= m_declProperties
[slot
];
937 if (prop
.attrs
& (AttrInitialSatisfiesTC
|AttrSystemInitialValue
)) continue;
938 auto const& tc
= prop
.typeConstraint
;
939 auto const index
= propSlotToIndex(slot
);
940 auto const rval
= m_declPropInit
[index
].val
;
941 if (type(rval
) == KindOfUninit
) continue;
943 if (tc
.isCheckable()) tc
.verifyProperty(&tv
, this, prop
.cls
, prop
.name
);
944 if (RuntimeOption::EvalEnforceGenericsUB
> 0) {
945 for (auto const& ub
: prop
.ubs
) {
946 if (ub
.isCheckable()) {
947 ub
.verifyProperty(&tv
, this, prop
.cls
, prop
.name
);
952 // No coercion for statically initialized properties.
953 assertx(type(tv
) == type(rval
));
954 assertx(val(tv
).num
== val(rval
).num
);
957 extra
->m_checkedPropInitialValues
.initWith(true);
960 void Class::checkPropTypeRedefinitions() const {
961 assertx(m_maybeRedefsPropTy
);
962 assertx(RuntimeOption::EvalCheckPropTypeHints
> 0);
964 assertx(m_extra
.get() != nullptr);
966 auto extra
= m_extra
.get();
967 checkedPropTypeRedefinesHandle(); // init handle
968 if (extra
->m_checkedPropTypeRedefs
.isInit()) return;
970 if (m_parent
->m_maybeRedefsPropTy
) m_parent
->checkPropTypeRedefinitions();
972 if (m_selfMaybeRedefsPropTy
) {
973 for (Slot slot
= 0; slot
< m_declProperties
.size(); slot
++) {
974 auto const& prop
= m_declProperties
[slot
];
975 if (prop
.attrs
& AttrNoBadRedeclare
) continue;
976 checkPropTypeRedefinition(slot
);
980 extra
->m_checkedPropTypeRedefs
.initWith(true);
983 void Class::checkPropTypeRedefinition(Slot slot
) const {
984 assertx(m_maybeRedefsPropTy
);
985 assertx(RuntimeOption::EvalCheckPropTypeHints
> 0);
987 assertx(slot
!= kInvalidSlot
);
988 assertx(slot
< numDeclProperties());
990 auto const& prop
= m_declProperties
[slot
];
991 assertx(!(prop
.attrs
& AttrNoBadRedeclare
));
993 auto const& oldProp
= m_parent
->m_declProperties
[slot
];
995 auto const& oldTC
= oldProp
.typeConstraint
;
996 auto const& newTC
= prop
.typeConstraint
;
998 if (!oldTC
.equivalentForProp(newTC
)) {
999 auto const oldTCName
=
1000 oldTC
.hasConstraint() ? oldTC
.displayName() : "mixed";
1001 auto const newTCName
=
1002 newTC
.hasConstraint() ? newTC
.displayName() : "mixed";
1004 auto const msg
= folly::sformat(
1005 "Type-hint of '{}::{}' must be {}{} (as in class {}), not {}",
1008 oldTC
.isUpperBound()? "upper-bounded by " : "",
1010 oldProp
.cls
->name(),
1013 raise_property_typehint_error(
1015 oldTC
.isSoft() && newTC
.isSoft(),
1016 oldTC
.isUpperBound() || newTC
.isUpperBound()
1020 if (RuntimeOption::EvalEnforceGenericsUB
> 0 &&
1021 (!prop
.ubs
.empty() || !oldProp
.ubs
.empty())) {
1022 std::vector
<TypeConstraint
> newTCs
= {newTC
};
1023 for (auto const& ub
: prop
.ubs
) newTCs
.push_back(ub
);
1024 std::vector
<TypeConstraint
> oldTCs
= {oldTC
};
1025 for (auto const& ub
: oldProp
.ubs
) oldTCs
.push_back(ub
);
1027 for (auto const& ub
: newTCs
) {
1028 if (std::none_of(oldTCs
.begin(), oldTCs
.end(),
1029 [&](const TypeConstraint
& tc
) {
1030 return tc
.equivalentForProp(ub
);
1032 auto const ubName
= ub
.hasConstraint() ? ub
.displayName() : "mixed";
1033 auto const msg
= folly::sformat(
1034 "Upper-bound {} of {}::{} has no equivalent upper-bound in {}",
1035 ubName
, prop
.cls
->name(), prop
.name
, oldProp
.cls
->name()
1037 raise_property_typehint_error(msg
, ub
.isSoft(), true);
1040 for (auto const& ub
: oldTCs
) {
1041 if (std::none_of(newTCs
.begin(), newTCs
.end(),
1042 [&](const TypeConstraint
& tc
) {
1043 return tc
.equivalentForProp(ub
);
1045 auto const ubName
= ub
.hasConstraint() ? ub
.displayName() : "mixed";
1046 auto const msg
= folly::sformat(
1047 "Upper-bound {} of {}::{} has no equivalent upper-bound in {}",
1048 ubName
, oldProp
.cls
->name(), oldProp
.name
, prop
.cls
->name()
1050 raise_property_typehint_error(msg
, ub
.isSoft(), true);
1056 ///////////////////////////////////////////////////////////////////////////////
1057 // Property storage.
1059 void Class::initSPropHandles() const {
1060 if (m_sPropCacheInit
.bound()) return;
1062 bool usePersistentHandles
= m_cachedClass
.isPersistent();
1063 bool allPersistentHandles
= usePersistentHandles
;
1065 // Propagate to parents so we can link inherited static props.
1066 Class
* parent
= this->parent();
1068 parent
->initSPropHandles();
1069 if (!rds::isPersistentHandle(parent
->sPropInitHandle())) {
1070 allPersistentHandles
= false;
1074 // Bind all the static prop handles.
1075 for (Slot slot
= 0, n
= m_staticProperties
.size(); slot
< n
; ++slot
) {
1076 auto& propHandle
= m_sPropCache
[slot
];
1077 auto const& sProp
= m_staticProperties
[slot
];
1079 if (sProp
.cls
== this || (sProp
.attrs
& AttrLSB
)) {
1080 if (usePersistentHandles
&& (sProp
.attrs
& AttrPersistent
)) {
1081 static_assert(sizeof(StaticPropData
) == sizeof(sProp
.val
),
1082 "StaticPropData must be a simple wrapper "
1083 "around TypedValue");
1085 rds::Mode::Persistent
,
1086 rds::SPropCache
{this, slot
},
1087 reinterpret_cast<const StaticPropData
*>(&sProp
.val
)
1092 rds::SPropCache
{this, slot
}
1096 auto const realSlot
= sProp
.cls
->lookupSProp(sProp
.name
);
1097 propHandle
= sProp
.cls
->m_sPropCache
[realSlot
];
1099 if (!propHandle
.isPersistent()) {
1100 allPersistentHandles
= false;
1104 // Bind the init handle; this indicates that all handles are bound.
1105 if (allPersistentHandles
) {
1106 // We must make sure the value stored at the handle is correct before
1107 // setting m_sPropCacheInit in case another thread tries to read it at just
1108 // the wrong time. And rather than giving each Class its own persistent
1109 // handle that always points to an immutable 'true', share one between all
1111 m_sPropCacheInit
= rds::s_persistentTrue
;
1113 m_sPropCacheInit
.bind(
1115 rds::LinkName
{"PropCacheInit", name()}
1118 rds::recordRds(m_sPropCacheInit
.handle(),
1119 sizeof(bool), "SPropCacheInit", name()->slice());
1122 Class::PropInitVec
* Class::getPropData() const {
1123 return (m_propDataCache
.bound() && m_propDataCache
.isInit())
1128 TypedValue
* Class::getSPropData(Slot index
) const {
1129 assertx(numStaticProperties() > index
);
1130 return m_sPropCache
[index
].bound()
1131 ? &m_sPropCache
[index
].get()->val
1136 ///////////////////////////////////////////////////////////////////////////////
1137 // Property lookup and accessibility.
1139 Class::PropSlotLookup
Class::getDeclPropSlot(
1141 const StringData
* key
1143 auto const propSlot
= lookupDeclProp(key
);
1145 auto accessible
= false;
1147 if (propSlot
!= kInvalidSlot
) {
1148 auto const attrs
= m_declProperties
[propSlot
].attrs
;
1149 if ((attrs
& (AttrProtected
|AttrPrivate
)) &&
1150 (g_context
.isNull() || !g_context
->debuggerSettings
.bypassCheck
)) {
1151 // Fetch the class in the inheritance tree which first declared the
1153 auto const baseClass
= m_declProperties
[propSlot
].cls
;
1156 // If ctx == baseClass, we have the right property and we can stop here.
1157 if (ctx
== baseClass
) return PropSlotLookup
{ propSlot
, true };
1159 // The anonymous context cannot access protected or private properties, so
1160 // we can fail fast here.
1161 if (ctx
== nullptr) return PropSlotLookup
{ propSlot
, false };
1164 if (attrs
& AttrPrivate
) {
1165 // ctx != baseClass and the property is private, so it is not
1166 // accessible. We need to keep going because ctx may define a private
1167 // property with this name.
1170 if (ctx
== (Class
*)-1 || ctx
->classof(baseClass
)) {
1171 // The special ctx (Class*)-1 is used by unserialization to
1172 // mean that protected properties are ok. Otherwise,
1173 // ctx is derived from baseClass, so we know this protected
1174 // property is accessible and we know ctx cannot have private
1175 // property with the same name, so we're done.
1176 return PropSlotLookup
{ propSlot
, true };
1178 if (!baseClass
->classof(ctx
)) {
1179 // ctx is not the same, an ancestor, or a descendent of baseClass,
1180 // so the property is not accessible. Also, we know that ctx cannot
1181 // be the same or an ancestor of this, so we don't need to check if
1182 // ctx declares a private property with the same name and we can
1184 return PropSlotLookup
{ propSlot
, false };
1186 // We now know this protected property is accessible, but we need to
1187 // keep going because ctx may define a private property with the same
1190 assertx(baseClass
->classof(ctx
));
1193 // The property is public (or we're in the debugger and we are bypassing
1194 // accessibility checks).
1196 // If ctx == this, we don't have to check if ctx defines a private
1197 // property with the same name and we can stop here.
1198 if (ctx
== this) return PropSlotLookup
{ propSlot
, true };
1200 // We still need to check if ctx defines a private property with the same
1204 // We didn't find a visible declared property in this's property map
1208 // If ctx is an ancestor of this, check if ctx has a private property with the
1210 if (ctx
&& ctx
!= (Class
*)-1 && classof(ctx
)) {
1211 auto const ctxPropSlot
= ctx
->lookupDeclProp(key
);
1213 if (ctxPropSlot
!= kInvalidSlot
&&
1214 ctx
->m_declProperties
[ctxPropSlot
].cls
== ctx
&&
1215 (ctx
->m_declProperties
[ctxPropSlot
].attrs
& AttrPrivate
)) {
1216 // A private property from ctx trumps any other property we may
1218 return PropSlotLookup
{ ctxPropSlot
, true };
1222 if (propSlot
== kInvalidSlot
&&
1223 !g_context
.isNull() &&
1224 g_context
->debuggerSettings
.bypassCheck
&&
1226 // If the property could not be located on the current class, and this
1227 // class has a parent class, and the current evaluation is a debugger
1228 // eval with bypassCheck == true, search for the property as a member of
1229 // the parent class. The debugger access is not subject to visibilty checks.
1230 return m_parent
->getDeclPropSlot(ctx
, key
);
1233 return PropSlotLookup
{ propSlot
, accessible
};
1236 Class::PropSlotLookup
Class::findSProp(
1238 const StringData
* sPropName
1240 auto const sPropInd
= lookupSProp(sPropName
);
1242 // Non-existent property.
1243 if (sPropInd
== kInvalidSlot
)
1244 return PropSlotLookup
{ kInvalidSlot
, false, false };
1246 auto const& sProp
= m_staticProperties
[sPropInd
];
1247 auto const sPropAttrs
= sProp
.attrs
;
1248 auto const sPropConst
= bool(sPropAttrs
& AttrIsConst
);
1249 const Class
* baseCls
= this;
1250 if (sPropAttrs
& AttrLSB
) {
1251 // For an LSB static, accessibility attributes are relative to the class
1252 // that originally declared it.
1253 baseCls
= sProp
.cls
;
1255 // Property access within this Class's context.
1256 if (ctx
== baseCls
) return PropSlotLookup
{ sPropInd
, true, sPropConst
};
1258 auto const accessible
= [&] {
1259 switch (sPropAttrs
& (AttrPublic
| AttrProtected
| AttrPrivate
)) {
1260 // Public properties are always accessible.
1264 // Property access is from within a parent class's method, which is
1265 // allowed for protected properties.
1267 return ctx
!= nullptr &&
1268 (baseCls
->classof(ctx
) || ctx
->classof(baseCls
));
1270 // Can only access private properties via the debugger.
1272 return g_context
->debuggerSettings
.bypassCheck
;
1279 return PropSlotLookup
{ sPropInd
, accessible
, sPropConst
};
1282 Class::PropValLookup
Class::getSPropIgnoreLateInit(
1284 const StringData
* sPropName
1288 auto const lookup
= findSProp(ctx
, sPropName
);
1289 if (lookup
.slot
== kInvalidSlot
) {
1290 return PropValLookup
{ nullptr, kInvalidSlot
, false, false };
1293 auto const sProp
= getSPropData(lookup
.slot
);
1296 auto const& decl
= m_staticProperties
[lookup
.slot
];
1297 auto const lateInit
= bool(decl
.attrs
& AttrLateInit
);
1300 sProp
&& (sProp
->m_type
!= KindOfUninit
|| lateInit
) &&
1301 "Static property initialization failed to initialize a property."
1304 if (sProp
->m_type
!= KindOfUninit
) {
1305 if (RuntimeOption::RepoAuthoritative
) {
1306 auto const repoTy
= staticPropRepoAuthType(lookup
.slot
);
1307 always_assert(tvMatchesRepoAuthType(*sProp
, repoTy
));
1310 if (RuntimeOption::EvalCheckPropTypeHints
> 2) {
1311 auto const typeOk
= [&]{
1313 !decl
.typeConstraint
.isCheckable() ||
1314 decl
.typeConstraint
.isSoft() ||
1315 (decl
.typeConstraint
.isUpperBound() &&
1316 RuntimeOption::EvalEnforceGenericsUB
< 2) ||
1317 (sProp
->m_type
== KindOfNull
&&
1318 !(decl
.attrs
& AttrNoImplicitNullable
));
1320 auto res
= skipCheck
? true : decl
.typeConstraint
.assertCheck(sProp
);
1321 if (RuntimeOption::EvalEnforceGenericsUB
>= 2) {
1322 for (auto const& ub
: decl
.ubs
) {
1323 if (ub
.isCheckable() && !ub
.isSoft()) {
1324 res
= res
&& ub
.assertCheck(sProp
);
1330 always_assert(typeOk
);
1336 PropValLookup
{ sProp
, lookup
.slot
, lookup
.accessible
, lookup
.constant
};
1339 Class::PropValLookup
Class::getSProp(
1341 const StringData
* sPropName
1343 auto const lookup
= getSPropIgnoreLateInit(ctx
, sPropName
);
1344 if (lookup
.val
&& UNLIKELY(lookup
.val
->m_type
== KindOfUninit
)) {
1345 auto const& decl
= m_staticProperties
[lookup
.slot
];
1346 if (decl
.attrs
& AttrLateInit
) {
1347 throw_late_init_prop(decl
.cls
, sPropName
, true);
1353 bool Class::IsPropAccessible(const Prop
& prop
, Class
* ctx
) {
1354 if (prop
.attrs
& AttrPublic
) return true;
1355 if (prop
.attrs
& AttrPrivate
) return prop
.cls
== ctx
;
1356 if (!ctx
) return false;
1358 return prop
.cls
->classof(ctx
) || ctx
->classof(prop
.cls
);
1361 Slot
Class::propIndexToSlot(uint16_t index
) const {
1362 auto const nprops
= m_declProperties
.size();
1363 for (Slot slot
= 0; slot
< nprops
; slot
++) {
1364 if (m_slotIndex
[slot
] == index
) return slot
;
1366 always_assert_flog(0, "propIndexToSlot: no slot found for index = {}", index
);
1369 ///////////////////////////////////////////////////////////////////////////////
1372 const StaticString
s_classname("classname");
1374 TypedValue
Class::clsCnsGet(const StringData
* clsCnsName
, ClsCnsLookup what
) const {
1376 auto cnsVal
= cnsNameToTV(clsCnsName
, clsCnsInd
, what
);
1377 if (!cnsVal
) return make_tv
<KindOfUninit
>();
1379 auto& cns
= m_constants
[clsCnsInd
];
1380 ArrayData
* typeCns
= nullptr;
1382 if (cnsVal
->m_type
!= KindOfUninit
) {
1384 // Type constants with the low bit set are already resolved and can be
1385 // returned after masking out that bit.
1387 // We can't check isHAMSafeDArray here because we're using that low bit as
1388 // a marker; instead, check isArrayLike and do the stricter check below.
1389 assertx(isArrayLikeType(type(cnsVal
)));
1390 assertx(!isRefcountedType(type(cnsVal
)));
1391 typeCns
= val(cnsVal
).parr
;
1392 auto const rawData
= reinterpret_cast<intptr_t>(typeCns
);
1393 if (rawData
& 0x1) {
1394 auto const resolved
= reinterpret_cast<ArrayData
*>(rawData
^ 0x1);
1395 assertx(resolved
->isHAMSafeDArray());
1396 return make_persistent_array_like_tv(resolved
);
1398 if (what
== ClsCnsLookup::IncludeTypesPartial
) {
1399 return make_tv
<KindOfUninit
>();
1407 * We use a sentinel static array to mark constants that are being evaluated
1408 * during recursive resolution. This array is never exposed to the rest of
1409 * the runtime, so we can test for it by pointer equality.
1411 static auto const s_sentinelVec
= PackedArray::CopyStatic(staticEmptyVec());
1412 assertx(s_sentinelVec
!= staticEmptyVec());
1415 * We have either a constant with a non-scalar initializer or an unresolved
1416 * type constant, meaning it will be potentially different in different
1417 * requests, which we store separately in an array in RDS.
1419 * We need a special marker value in the non-scalar constant cache to indicate
1420 * that we're currently evaluating the value of a (type) constant. If we
1421 * attempt to evaluate the value of a (type) constant, and the special marker
1422 * is present, that means the (type) constant is recursively defined and
1423 * we'll raise an error. The globals array is never a valid value of a (type)
1424 * constant, so we use it as the marker.
1426 m_nonScalarConstantCache
.bind(
1428 rds::LinkName
{"ClassNonScalarCnsCache", name()}
1430 auto& clsCnsData
= *m_nonScalarConstantCache
;
1431 if (m_nonScalarConstantCache
.isInit()) {
1432 auto const cCns
= clsCnsData
->get(clsCnsName
);
1433 if (cCns
.is_init()) {
1434 // There's an entry in the cache for this (type) constant. If it's the
1435 // sentinel value, the constant is recursively defined - throw an error.
1436 // Otherwise, return the cached result.
1437 if (UNLIKELY(tvIsVec(cCns
) && val(cCns
).parr
== s_sentinelVec
)) {
1440 "Cannot declare self-referencing {} '{}::{}'",
1441 cns
.isType() ? "type constant" : "constant",
1450 // Because RDS uses a generation number scheme, when isInit was false we
1451 // may have a pointer to a (no-longer extant) ArrayData in our RDS entry.
1452 // Use detach to clear our Array so we don't attempt to decref the stale
1454 clsCnsData
.detach();
1455 clsCnsData
= Array::attach(
1456 MixedArray::MakeReserveDict(m_constants
.size())
1458 m_nonScalarConstantCache
.markInit();
1461 // We're going to run the 86cinit to get the constant's value, or try to
1462 // resolve the type constant. Store the sentinel value as this (type)
1463 // constant's cache entry // so that we can detect recursion.
1464 auto marker
= make_array_like_tv(s_sentinelVec
);
1465 clsCnsData
.set(StrNR(clsCnsName
), tvAsCVarRef(&marker
), true /* isKey */);
1468 // Resolve type constant, if needed
1469 if (what
== ClsCnsLookup::IncludeTypesPartial
) {
1470 return make_tv
<KindOfUninit
>();
1473 bool persistent
= true;
1475 resolvedTS
= TypeStructure::resolve(typeCns
, cns
.name
, cns
.cls
, this,
1477 } catch (const Exception
& e
) {
1478 raise_error(e
.getMessage());
1480 assertx(resolvedTS
.isHAMSafeDArray());
1482 auto const ad
= ArrayData::GetScalarArray(std::move(resolvedTS
));
1484 auto const rawData
= reinterpret_cast<intptr_t>(ad
);
1485 assertx((rawData
& 0x7) == 0 && "ArrayData not 8-byte aligned");
1486 auto taggedData
= reinterpret_cast<ArrayData
*>(rawData
| 0x1);
1488 // Multiple threads might create and store the resolved type structure
1489 // here, but that's fine since they'll all store the same thing thanks to
1490 // GetScalarArray(). Ditto for the pointed class.
1491 // We could avoid a little duplicated work during warmup with more
1492 // complexity but it's not worth it.
1493 auto& cns_nc
= const_cast<Const
&>(cns
);
1494 auto const classname_field
= ad
->get(s_classname
.get());
1495 if (isStringType(classname_field
.type())) {
1496 cns_nc
.setPointedClsName(classname_field
.val().pstr
);
1498 cns_nc
.val
.m_data
.parr
= taggedData
;
1499 return make_persistent_array_like_tv(ad
);
1502 auto tv
= make_persistent_array_like_tv(ad
);
1503 clsCnsData
.set(StrNR(clsCnsName
), tvAsCVarRef(&tv
), true /* isKey */);
1507 // The class constant has not been initialized yet; do so.
1508 auto const meth86cinit
= cns
.cls
->lookupMethod(s_86cinit
.get());
1509 TypedValue args
[1] = {
1510 make_tv
<KindOfPersistentString
>(const_cast<StringData
*>(cns
.name
.get()))
1513 auto ret
= g_context
->invokeFuncFew(
1515 const_cast<Class
*>(this),
1522 assertx(tvAsCVarRef(&ret
).isAllowedAsConstantValue());
1523 clsCnsData
.set(StrNR(clsCnsName
), tvAsCVarRef(ret
), true /* isKey */);
1525 // The caller will inc-ref the returned value, so undo the inc-ref caused by
1526 // storing it in the cache.
1527 tvDecRefGenNZ(&ret
);
1531 const TypedValue
* Class::cnsNameToTV(const StringData
* clsCnsName
,
1533 ClsCnsLookup what
) const {
1534 clsCnsInd
= m_constants
.findIndex(clsCnsName
);
1535 if (clsCnsInd
== kInvalidSlot
) {
1538 if (m_constants
[clsCnsInd
].isAbstract()) {
1541 if (what
== ClsCnsLookup::NoTypes
&& m_constants
[clsCnsInd
].isType()) {
1544 auto const ret
= &m_constants
[clsCnsInd
].val
;
1545 assertx(m_constants
[clsCnsInd
].isType() || tvIsPlausible(*ret
));
1549 Slot
Class::clsCnsSlot(
1550 const StringData
* name
, bool wantTypeCns
, bool allowAbstract
1552 auto slot
= m_constants
.findIndex(name
);
1553 if (slot
== kInvalidSlot
) return slot
;
1554 if (!allowAbstract
&& m_constants
[slot
].isAbstract()) return kInvalidSlot
;
1555 return m_constants
[slot
].isType() == wantTypeCns
? slot
: kInvalidSlot
;
1558 DataType
Class::clsCnsType(const StringData
* cnsName
) const {
1560 auto const cns
= cnsNameToTV(cnsName
, slot
);
1561 // TODO(#2913342): lookup the constant in RDS in case it's dynamic
1562 // and already initialized.
1563 if (!cns
) return KindOfUninit
;
1567 ///////////////////////////////////////////////////////////////////////////////
1570 bool Class::verifyPersistent() const {
1571 if (!(attrs() & AttrPersistent
)) return false;
1572 if (m_parent
.get() && !classHasPersistentRDS(m_parent
.get())) {
1575 int numIfaces
= m_interfaces
.size();
1576 for (int i
= 0; i
< numIfaces
; i
++) {
1577 if (!classHasPersistentRDS(m_interfaces
[i
])) {
1581 for (auto const& usedTrait
: m_extra
->m_usedTraits
) {
1582 if (!classHasPersistentRDS(usedTrait
.get())) {
1589 void Class::setInstanceBits() {
1590 setInstanceBitsImpl
<false>();
1592 void Class::setInstanceBitsAndParents() {
1593 setInstanceBitsImpl
<true>();
1596 template<bool setParents
>
1597 void Class::setInstanceBitsImpl() {
1598 // Bit 0 is reserved to indicate whether or not the rest of the bits
1599 // are initialized yet.
1600 if (m_instanceBits
.test(0)) return;
1602 InstanceBits::BitSet bits
;
1604 auto setBits
= [&](Class
* c
) {
1605 if (setParents
) c
->setInstanceBitsAndParents();
1606 bits
|= c
->m_instanceBits
;
1608 if (m_parent
.get()) setBits(m_parent
.get());
1610 int numIfaces
= m_interfaces
.size();
1611 for (int i
= 0; i
< numIfaces
; i
++) setBits(m_interfaces
[i
]);
1613 // XXX: this assert fails on the initFlag; oops.
1614 if (unsigned bit
= InstanceBits::lookup(m_preClass
->name())) {
1617 m_instanceBits
= bits
;
1621 const ReifiedGenericsInfo k_defaultReifiedGenericsInfo
{0, false, 0, {}};
1624 const ReifiedGenericsInfo
& Class::getReifiedGenericsInfo() const {
1625 if (!m_hasReifiedGenerics
) return k_defaultReifiedGenericsInfo
;
1627 return m_extra
.raw()->m_reifiedGenericsInfo
;
1630 ///////////////////////////////////////////////////////////////////////////////
1633 // These are mostly for the class creation path.
1635 void Class::setParent() {
1636 // Cache m_preClass->attrs()
1637 m_attrCopy
= m_preClass
->attrs();
1639 // Validate the parent
1640 if (m_parent
.get() != nullptr) {
1641 Attr parentAttrs
= m_parent
->attrs();
1642 if (UNLIKELY(parentAttrs
&
1643 (AttrFinal
| AttrInterface
| AttrTrait
| AttrEnum
))) {
1644 if (!(parentAttrs
& AttrFinal
) ||
1645 (parentAttrs
& AttrEnum
) ||
1646 m_preClass
->userAttributes().find(s___MockClass
.get()) ==
1647 m_preClass
->userAttributes().end() ||
1648 m_parent
->isCollectionClass()) {
1649 raise_error("Class %s may not inherit from %s (%s)",
1650 m_preClass
->name()->data(),
1651 ((parentAttrs
& AttrEnum
) ? "enum" :
1652 (parentAttrs
& AttrFinal
) ? "final class" :
1653 (parentAttrs
& AttrInterface
) ? "interface" : "trait"),
1654 m_parent
->name()->data());
1656 if ((parentAttrs
& AttrAbstract
) &&
1657 ((m_attrCopy
& (AttrAbstract
|AttrFinal
)) != (AttrAbstract
|AttrFinal
))) {
1659 "Class %s with %s inheriting 'abstract final' class %s"
1660 " must also be 'abstract final'",
1661 m_preClass
->name()->data(),
1662 s___MockClass
.data(),
1663 m_parent
->name()->data()
1667 if ((m_attrCopy
& AttrIsConst
) && !(parentAttrs
& AttrIsConst
)) {
1669 "Const class %s cannot extend non-const parent %s",
1670 m_preClass
->name()->data(),
1671 m_parent
->name()->data()
1674 m_preClass
->enforceInMaybeSealedParentWhitelist(m_parent
->preClass());
1675 if (m_parent
->m_maybeRedefsPropTy
) m_maybeRedefsPropTy
= true;
1678 // Handle stuff specific to cppext classes
1679 if (m_parent
.get() && m_parent
->m_extra
->m_instanceCtor
) {
1681 m_extra
.raw()->m_instanceCtor
= m_parent
->m_extra
->m_instanceCtor
;
1682 m_extra
.raw()->m_instanceCtorUnlocked
=
1683 m_parent
->m_extra
->m_instanceCtorUnlocked
;
1684 m_extra
.raw()->m_instanceDtor
= m_parent
->m_extra
->m_instanceDtor
;
1685 assertx(m_parent
->m_releaseFunc
== m_parent
->m_extra
->m_instanceDtor
);
1686 m_releaseFunc
= m_parent
->m_extra
->m_instanceDtor
;
1687 // XXX: should this be copying over the clsInfo also? Might be broken...
1691 static Func
* findSpecialMethod(Class
* cls
, const StringData
* name
) {
1692 if (!cls
->preClass()->hasMethod(name
)) return nullptr;
1693 Func
* f
= cls
->preClass()->lookupMethod(name
);
1697 f
->setHasPrivateAncestor(false);
1702 s_toString("__toString"),
1703 s_construct("__construct"),
1704 s_invoke("__invoke"),
1706 s_debugInfo("__debugInfo"),
1709 void Class::setSpecial() {
1710 m_toString
= lookupMethod(s_toString
.get());
1713 * The invoke method is only cached in the Class for a fast path JIT
1714 * translation. If someone defines a weird __invoke (e.g. as a
1715 * static method), we don't bother caching it here so the translated
1716 * code won't have to check for that case.
1718 * Note that AttrStatic on a closure's __invoke Func* means it is a
1719 * static closure---but the call to __invoke still works as if it
1720 * were a non-static method call---so they are excluded from that
1721 * here. (The closure prologue uninstalls the $this and installs
1722 * the appropriate static context.)
1724 m_invoke
= lookupMethod(s_invoke
.get());
1725 if (m_invoke
&& m_invoke
->isStaticInPrologue()) {
1729 // Look for __construct() declared in either this class or a trait
1730 auto const func
= lookupMethod(s_construct
.get());
1731 if (func
&& func
->cls() == this) {
1732 if (func
->takesInOutParams()) {
1733 raise_error("Parameters may not be marked inout on constructors");
1740 // Look for parent constructor.
1741 if (m_parent
.get() != nullptr && m_parent
->m_ctor
) {
1742 m_ctor
= m_parent
->m_ctor
;
1746 if (UNLIKELY(!SystemLib::s_nullCtor
)) {
1747 SystemLib::setupNullCtor(this);
1750 m_ctor
= SystemLib::s_nullCtor
;
1755 inline void raiseIncompat(const PreClass
* implementor
,
1756 const Func
* imeth
) {
1757 const char* name
= imeth
->name()->data();
1758 raise_error("Declaration of %s::%s() must be compatible with "
1760 implementor
->name()->data(), name
,
1761 imeth
->cls()->preClass()->name()->data(), name
);
1764 inline void checkRefCompat(const char* kind
, const Func
* self
,
1765 const Func
* inherit
) {
1766 // Shadowing is okay, if we inherit a private method we can't access it
1768 if (inherit
->attrs() & AttrPrivate
) return;
1770 if (!self
->takesInOutParams() && !inherit
->takesInOutParams()) return;
1772 auto const max
= std::max(
1773 self
->numNonVariadicParams(),
1774 inherit
->numNonVariadicParams()
1777 for (int i
= 0; i
< max
; ++i
) {
1778 // Since we're looking at ref wrappers of inout functions we need to check
1779 // inout, but if one of the functions isn't a wrapper we do actually have
1781 auto const smode
= self
->isInOut(i
);
1782 auto const imode
= inherit
->isInOut(i
);
1783 if (smode
!= imode
) {
1784 auto const msg
= [&] {
1785 auto const sname
= self
->fullName()->data();
1786 auto const iname
= inherit
->fullName()->data();
1790 i
>= inherit
->numNonVariadicParams() ? "" : "inout ";
1791 return folly::sformat(
1792 "Parameter {} on function {} was declared inout but is not "
1793 "declared {}on {} function {}", i
+ 1, sname
, idecl
,
1796 auto const sdecl
= i
>= self
->numNonVariadicParams() ? "" : "inout ";
1797 return folly::sformat(
1798 "Parameter {} on function {} was not declared {}but is "
1799 "declared inout on {} function {}", i
+ 1, sname
, sdecl
,
1808 // Check compatibility vs interface and abstract declarations
1809 void checkDeclarationCompat(const PreClass
* preClass
,
1810 const Func
* func
, const Func
* imeth
) {
1811 const Func::ParamInfoVec
& params
= func
->params();
1812 const Func::ParamInfoVec
& iparams
= imeth
->params();
1814 auto const ivariadic
= imeth
->hasVariadicCaptureParam();
1815 if (ivariadic
&& !func
->hasVariadicCaptureParam()) {
1816 raiseIncompat(preClass
, imeth
);
1819 // Verify that meth has at least as many parameters as imeth.
1820 if (func
->numParams() < imeth
->numParams()) {
1821 // This check doesn't require special casing for variadics, because
1822 // it's not ok to turn a variadic function into a non-variadic.
1823 raiseIncompat(preClass
, imeth
);
1825 // Verify that the typehints for meth's parameters are compatible with
1826 // imeth's corresponding parameter typehints.
1827 size_t firstOptional
= 0;
1830 for (; i
< imeth
->numNonVariadicParams(); ++i
) {
1831 auto const& p
= params
[i
];
1832 if (p
.isVariadic()) { raiseIncompat(preClass
, imeth
); }
1833 if (!iparams
[i
].hasDefaultValue()) {
1834 // The leftmost of imeth's contiguous trailing optional parameters
1835 // must start somewhere to the right of this parameter (which may
1836 // be the variadic param)
1837 firstOptional
= i
+ 1;
1840 assertx(!ivariadic
|| iparams
[iparams
.size() - 1].isVariadic());
1841 assertx(!ivariadic
|| params
[params
.size() - 1].isVariadic());
1844 // Verify that meth provides defaults, starting with the parameter that
1845 // corresponds to the leftmost of imeth's contiguous trailing optional
1846 // parameters and *not* including any variadic last param (variadics
1847 // don't have any default values).
1848 for (unsigned i
= firstOptional
; i
< func
->numNonVariadicParams(); ++i
) {
1849 if (!params
[i
].hasDefaultValue()) {
1850 raiseIncompat(preClass
, imeth
);
1855 imeth
->attrs() & AttrAbstract
? "abstract" : "interface",
1863 Class::Class(PreClass
* preClass
, Class
* parent
,
1864 VMCompactVector
<ClassPtr
>&& usedTraits
,
1865 unsigned classVecLen
, unsigned funcVecLen
)
1866 : m_releaseFunc
{ObjectData::release
}
1867 , m_preClass(PreClassPtr(preClass
))
1868 , m_classVecLen(always_safe_cast
<decltype(m_classVecLen
)>(classVecLen
))
1869 , m_funcVecLen(always_safe_cast
<decltype(m_funcVecLen
)>(funcVecLen
))
1870 , m_maybeRedefsPropTy
{false}
1871 , m_selfMaybeRedefsPropTy
{false}
1872 , m_needsPropInitialCheck
{false}
1873 , m_hasReifiedGenerics
{false}
1874 , m_hasReifiedParent
{false}
1875 , m_serialized(false)
1881 if (usedTraits
.size()) {
1883 m_extra
.raw()->m_usedTraits
= std::move(usedTraits
);
1887 setSpecial(); // must run before setRTAttributes
1891 setProperties(); // must run before setInitializers
1896 setNativeDataInfo();
1899 setInstanceMemoCacheInfo();
1900 setLSBMemoCacheInfo();
1902 // A class is allowed to implement two interfaces that share the same slot if
1903 // we'll fatal trying to define that class, so this has to happen after all
1904 // of those fatals could be thrown.
1905 setInterfaceVtables();
1907 // Calculates the base pointer offset and
1908 // the MemoryManager index of the class size.
1912 void Class::methodOverrideCheck(const Func
* parentMethod
, const Func
* method
) {
1913 // Skip special methods
1914 if (method
->isGenerated()) return;
1916 if ((parentMethod
->attrs() & AttrFinal
)) {
1917 if (m_preClass
->userAttributes().find(s___MockClass
.get()) ==
1918 m_preClass
->userAttributes().end()) {
1919 raise_error("Cannot override final method %s::%s()",
1920 m_parent
->name()->data(), parentMethod
->name()->data());
1924 if ((method
->attrs() & AttrAbstract
) &&
1925 !(parentMethod
->attrs() & AttrAbstract
) &&
1926 !method
->isFromTrait()) {
1927 raise_error("Cannot re-declare non-abstract method %s::%s() abstract in "
1929 m_parent
->m_preClass
->name()->data(),
1930 parentMethod
->name()->data(), m_preClass
->name()->data());
1933 if ((method
->attrs() & (AttrPublic
| AttrProtected
| AttrPrivate
)) >
1934 (parentMethod
->attrs() & (AttrPublic
| AttrProtected
| AttrPrivate
))) {
1936 "Access level to %s::%s() must be %s (as in class %s) or weaker",
1937 m_preClass
->name()->data(), method
->name()->data(),
1938 attrToVisibilityStr(parentMethod
->attrs()),
1939 m_parent
->name()->data());
1942 if ((method
->attrs() & AttrStatic
) != (parentMethod
->attrs() & AttrStatic
)) {
1943 raise_error("Cannot change %sstatic method %s::%s() to %sstatic in %s",
1944 (parentMethod
->attrs() & AttrStatic
) ? "" : "non-",
1945 parentMethod
->baseCls()->name()->data(),
1946 method
->name()->data(),
1947 (method
->attrs() & AttrStatic
) ? "" : "non-",
1948 m_preClass
->name()->data());
1951 Func
* baseMethod
= parentMethod
->baseCls()->lookupMethod(method
->name());
1952 if (!(method
->attrs() & AttrAbstract
) &&
1953 (baseMethod
->attrs() & AttrAbstract
)) {
1954 checkDeclarationCompat(m_preClass
.get(), method
, baseMethod
);
1956 checkRefCompat("parent", method
, parentMethod
);
1960 void Class::setMethods() {
1961 MethodMapBuilder builder
;
1963 ITRACE(5, "----------\nsetMethods() for {}:\n", this->name()->data());
1964 if (m_parent
.get() != nullptr) {
1965 // Copy down the parent's method entries. These may be overridden below.
1966 for (Slot i
= 0; i
< m_parent
->m_methods
.size(); ++i
) {
1967 Func
* f
= m_parent
->getMethod(i
);
1969 ITRACE(5, " - adding parent method {}\n", f
->name()->data());
1970 assertx(builder
.size() == i
);
1971 builder
.add(f
->name(), f
);
1975 static_assert(AttrPublic
< AttrProtected
&& AttrProtected
< AttrPrivate
, "");
1976 // Overlay/append this class's public/protected methods onto/to those of the
1978 for (size_t methI
= 0; methI
< m_preClass
->numMethods(); ++methI
) {
1979 Func
* method
= m_preClass
->methods()[methI
];
1980 ITRACE(5, " - processing pre-class method {}\n", method
->name()->data());
1981 if (Func::isSpecial(method
->name())) {
1982 if (method
->name() == s_86sinit
.get() ||
1983 method
->name() == s_86pinit
.get()) {
1985 * we could also skip the cinit function here, but
1986 * that would mean storing it somewhere else.
1992 MethodMapBuilder::iterator it2
= builder
.find(method
->name());
1993 if (it2
!= builder
.end()) {
1994 Func
* parentMethod
= builder
[it2
->second
];
1995 // We should never have null func pointers to deal with
1996 assertx(parentMethod
);
1997 // An abstract method that came from a trait doesn't override another
1999 if (method
->isFromTrait() && (method
->attrs() & AttrAbstract
)) continue;
2000 methodOverrideCheck(parentMethod
, method
);
2002 Func
* f
= method
->clone(this);
2005 assertx(!(f
->attrs() & AttrPrivate
) ||
2006 (parentMethod
->attrs() & AttrPrivate
));
2007 if ((parentMethod
->attrs() & AttrPrivate
) || (f
->attrs() & AttrPrivate
)) {
2010 baseClass
= parentMethod
->baseCls();
2012 f
->setBaseCls(baseClass
);
2013 f
->setHasPrivateAncestor(
2014 parentMethod
->hasPrivateAncestor() ||
2015 (parentMethod
->attrs() & AttrPrivate
));
2016 builder
[it2
->second
] = f
;
2018 // This is the first class that declares the method
2019 Class
* baseClass
= this;
2021 Func
* f
= method
->clone(this);
2023 f
->setBaseCls(baseClass
);
2024 f
->setHasPrivateAncestor(false);
2025 builder
.add(method
->name(), f
);
2029 // Add 86reifiedinit to base classes that do not have AttrNoReifiedInit set
2030 if (m_parent
.get() == nullptr && !(attrs() & AttrNoReifiedInit
)) {
2031 assertx(builder
.find(s_86reifiedinit
.get()) == builder
.end());
2032 auto f
= SystemLib::getNull86reifiedinit(this);
2033 builder
.add(f
->name(), f
);
2036 auto const traitsBeginIdx
= builder
.size();
2037 if (m_extra
->m_usedTraits
.size()) {
2038 importTraitMethods(builder
);
2040 auto const traitsEndIdx
= builder
.size();
2043 m_extra
.raw()->m_traitsBeginIdx
= traitsBeginIdx
;
2044 m_extra
.raw()->m_traitsEndIdx
= traitsEndIdx
;
2047 // If class is not abstract, check that all abstract methods have been defined
2048 if (!(attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
))) {
2049 for (Slot i
= 0; i
< builder
.size(); i
++) {
2050 const Func
* meth
= builder
[i
];
2051 if (meth
->attrs() & AttrAbstract
) {
2052 raise_error("Class %s contains abstract method (%s) and "
2053 "must therefore be declared abstract or implement "
2054 "the remaining methods", m_preClass
->name()->data(),
2055 meth
->name()->data());
2060 // If class is abstract final, its static methods should not be abstract
2061 if ((attrs() & (AttrAbstract
| AttrFinal
)) == (AttrAbstract
| AttrFinal
)) {
2062 for (Slot i
= 0; i
< builder
.size(); i
++) {
2063 const Func
* meth
= builder
[i
];
2064 if ((meth
->attrs() & (AttrAbstract
| AttrStatic
))
2065 == (AttrAbstract
| AttrStatic
)) {
2067 "Class %s contains abstract static method (%s) and "
2068 "therefore cannot be declared 'abstract final'",
2069 m_preClass
->name()->data(), meth
->name()->data());
2074 builder
.create(m_methods
);
2075 for (Slot i
= 0; i
< builder
.size(); ++i
) {
2076 builder
[i
]->setMethodSlot(i
);
2078 setFuncVec(builder
);
2082 * Initialize m_RTAttrs by inspecting the class methods and parents.
2084 void Class::setRTAttributes() {
2086 if (lookupMethod(s_sleep
.get())) { m_RTAttrs
|= Class::HasSleep
; }
2087 if (lookupMethod(s_clone
.get())) { m_RTAttrs
|= Class::HasClone
; }
2089 if ((isBuiltin() && Native::getNativePropHandler(name())) ||
2090 (m_parent
&& m_parent
->hasNativePropHandler())) {
2091 m_RTAttrs
|= Class::HasNativePropHandler
;
2095 void Class::setConstants() {
2096 ConstMap::Builder builder
;
2098 if (m_parent
.get() != nullptr) {
2099 for (Slot i
= 0; i
< m_parent
->m_constants
.size(); ++i
) {
2100 // Copy parent's constants.
2101 builder
.add(m_parent
->m_constants
[i
].name
, m_parent
->m_constants
[i
]);
2105 // Copy in interface constants.
2106 for (int i
= 0, size
= m_interfaces
.size(); i
< size
; ++i
) {
2107 const Class
* iface
= m_interfaces
[i
];
2109 for (Slot slot
= 0; slot
< iface
->m_constants
.size(); ++slot
) {
2110 auto const iConst
= iface
->m_constants
[slot
];
2112 // If you're inheriting a constant with the same name as an existing
2113 // one, they must originate from the same place, unless the constant
2114 // was defined as abstract.
2115 auto const existing
= builder
.find(iConst
.name
);
2117 if (existing
== builder
.end()) {
2118 builder
.add(iConst
.name
, iConst
);
2121 auto& existingConst
= builder
[existing
->second
];
2123 if (iConst
.isType() != existingConst
.isType()) {
2124 raise_error("%s cannot inherit the %sconstant %s from %s, because it "
2125 "was previously inherited as a %sconstant from %s",
2126 m_preClass
->name()->data(),
2127 iConst
.isType() ? "type " : "",
2128 iConst
.name
->data(),
2129 iConst
.cls
->name()->data(),
2130 iConst
.isType() ? "" : "type ",
2131 existingConst
.cls
->name()->data());
2134 if (iConst
.isAbstract()) {
2138 if (existingConst
.isAbstract()) {
2139 existingConst
.cls
= iConst
.cls
;
2140 existingConst
.val
= iConst
.val
;
2144 if (existingConst
.cls
!= iConst
.cls
) {
2145 // It's only an error if the constant comes from the declared
2147 for (auto const& interface
: m_declInterfaces
) {
2148 if (interface
.get() == iface
) {
2149 raise_error("%s cannot inherit the %sconstant %s from %s, because "
2150 "it was previously inherited from %s",
2151 m_preClass
->name()->data(),
2152 iConst
.isType() ? "type " : "",
2153 iConst
.name
->data(),
2154 iConst
.cls
->name()->data(),
2155 existingConst
.cls
->name()->data());
2162 for (Slot i
= 0, sz
= m_preClass
->numConstants(); i
< sz
; ++i
) {
2163 const PreClass::Const
* preConst
= &m_preClass
->constants()[i
];
2164 ConstMap::Builder::iterator it2
= builder
.find(preConst
->name());
2165 if (it2
!= builder
.end()) {
2166 auto definingClass
= builder
[it2
->second
].cls
;
2167 // Forbid redefining constants from interfaces, but not superclasses.
2168 // Constants from interfaces implemented by superclasses can be
2170 if (definingClass
->attrs() & AttrInterface
) {
2171 for (auto interface
: m_declInterfaces
) {
2172 if (interface
->hasConstant(preConst
->name()) ||
2173 interface
->hasTypeConstant(preConst
->name())) {
2174 raise_error("Cannot override previously defined %sconstant "
2176 builder
[it2
->second
].isType() ? "type " : "",
2177 builder
[it2
->second
].cls
->name()->data(),
2178 preConst
->name()->data(),
2179 m_preClass
->name()->data());
2184 if (preConst
->isAbstract() &&
2185 !builder
[it2
->second
].isAbstract()) {
2186 raise_error("Cannot re-declare as abstract previously defined "
2187 "%sconstant %s::%s in %s",
2188 builder
[it2
->second
].isType() ? "type " : "",
2189 builder
[it2
->second
].cls
->name()->data(),
2190 preConst
->name()->data(),
2191 m_preClass
->name()->data());
2194 if (preConst
->isType() != builder
[it2
->second
].isType()) {
2195 raise_error("Cannot re-declare as a %sconstant previously defined "
2196 "%sconstant %s::%s in %s",
2197 preConst
->isType() ? "type " : "",
2198 preConst
->isType() ? "" : "type ",
2199 builder
[it2
->second
].cls
->name()->data(),
2200 preConst
->name()->data(),
2201 m_preClass
->name()->data());
2203 builder
[it2
->second
].cls
= this;
2204 builder
[it2
->second
].val
= preConst
->val();
2208 constant
.cls
= this;
2209 constant
.name
= preConst
->name();
2210 constant
.val
= preConst
->val();
2211 builder
.add(preConst
->name(), constant
);
2215 // If class is not abstract, all abstract constants should have been
2217 if (!(attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
))) {
2218 for (Slot i
= 0; i
< builder
.size(); i
++) {
2219 const Const
& constant
= builder
[i
];
2220 if (constant
.isAbstract()) {
2221 raise_error("Class %s contains abstract %sconstant (%s) and "
2222 "must therefore be declared abstract or define "
2223 "the remaining constants",
2224 m_preClass
->name()->data(),
2225 constant
.isType() ? "type " : "",
2226 constant
.name
->data());
2231 // If class is abstract final, its constants should not be abstract
2233 (attrs() & (AttrAbstract
| AttrFinal
)) == (AttrAbstract
| AttrFinal
)) {
2234 for (Slot i
= 0; i
< builder
.size(); i
++) {
2235 const Const
& constant
= builder
[i
];
2236 if (constant
.isAbstract()) {
2238 "Class %s contains abstract %sconstant (%s) and "
2239 "therefore cannot be declared 'abstract final'",
2240 m_preClass
->name()->data(),
2241 constant
.isType() ? "type " : "",
2242 constant
.name
->data());
2248 // For type constants, we have to use the value from the PreClass of the
2249 // declaring class, because the parent class or interface we got it from may
2250 // have replaced it with a resolved value.
2251 for (auto& pair
: builder
) {
2252 auto& cns
= builder
[pair
.second
];
2254 auto& preConsts
= cns
.cls
->preClass()->constantsMap();
2255 auto const idx
= preConsts
.findIndex(cns
.name
.get());
2257 cns
.val
= preConsts
[idx
].val();
2260 cns
.pointedClsName
= nullptr;
2264 m_constants
.create(builder
);
2269 void copyDeepInitAttr(const PreClass::Prop
* pclsProp
, Class::Prop
* clsProp
) {
2270 if (pclsProp
->attrs() & AttrDeepInit
) {
2271 clsProp
->attrs
= (Attr
)(clsProp
->attrs
| AttrDeepInit
);
2273 clsProp
->attrs
= (Attr
)(clsProp
->attrs
& ~AttrDeepInit
);
2278 * KeyFn should be a function that takes an index and returns a key to sort by.
2279 * To sort lexicographically by multiple fields, the key can be a tuple type.
2281 template<typename KeyFn
>
2282 void sortByKey(std::vector
<uint32_t>& indices
, KeyFn keyFn
) {
2283 std::sort(indices
.begin(), indices
.end(), [&](uint32_t a
, uint32_t b
) {
2284 return keyFn(a
) < keyFn(b
);
2290 void Class::sortOwnProps(const PropMap::Builder
& curPropMap
,
2293 std::vector
<uint16_t>& slotIndex
) {
2294 auto const serverMode
= RuntimeOption::ServerExecutionMode();
2295 FTRACE(3, "Class::sortOwnProps: PreClass: {}\n", m_preClass
->name()->data());
2296 if (serverMode
&& RuntimeOption::ServerLogReorderProps
) {
2297 Logger::FInfo("Class::sortOwnProps: PreClass: {}",
2298 m_preClass
->name()->data());
2300 auto const size
= past
- first
;
2301 if (size
== 0) return; // no own props
2302 std::vector
<uint32_t> order(size
);
2303 for (uint32_t i
= 0; i
< size
; i
++) {
2304 order
[i
] = first
+ i
;
2306 // We don't change the order of the properties for closures.
2307 if (c_Closure::initialized() && parent() != c_Closure::classof()) {
2308 if (RuntimeOption::EvalReorderProps
== "hotness") {
2309 /* Sort the properties in decreasing order of their profile counts,
2310 * according to the serialized JIT profile data. In case of ties, we
2311 * preserve the logical order among the properties. Note that, in the
2312 * absence of profile data (e.g. if jumpstart failed to deserialize the
2313 * JIT profile data), then the physical layout of the properties in memory
2314 * will match their logical order. */
2315 sortByKey(order
, [&](uint32_t index
) {
2316 auto const& prop
= curPropMap
[index
];
2317 int64_t count
= PropertyProfile::getCount(prop
.cls
->name(), prop
.name
);
2318 return std::make_tuple(-count
, index
);
2320 } else if (RuntimeOption::EvalReorderProps
== "alphabetical") {
2321 std::sort(order
.begin(), order
.end(),
2322 [&] (uint32_t a
, uint32_t b
) {
2323 auto const& propa
= curPropMap
[a
];
2324 auto const& propb
= curPropMap
[b
];
2325 return strcmp(propa
.name
->data(), propb
.name
->data()) < 0;
2327 } else if (RuntimeOption::EvalReorderProps
== "countedness") {
2328 // Countable properties come earlier. Break ties by logical order.
2329 sortByKey(order
, [&](uint32_t index
) {
2330 auto const& prop
= curPropMap
[index
];
2331 int64_t countable
= jit::irgen::propertyMayBeCountable(prop
);
2332 return std::make_tuple(-countable
, index
);
2334 } else if (RuntimeOption::EvalReorderProps
== "countedness-hotness") {
2335 // Countable properties come earlier. Break ties by profile counts
2336 // (assuming that we have them from jumpstart), then by logical order.
2337 sortByKey(order
, [&](uint32_t index
) {
2338 auto const& prop
= curPropMap
[index
];
2339 int64_t count
= PropertyProfile::getCount(prop
.cls
->name(), prop
.name
);
2340 int64_t countable
= jit::irgen::propertyMayBeCountable(prop
);
2341 return std::make_tuple(-countable
, -count
, index
);
2345 assertx(slotIndex
.size() == past
);
2346 for (uint32_t i
= 0; i
< size
; i
++) {
2347 auto slot
= order
[i
];
2348 auto index
= first
+ i
;
2349 slotIndex
[slot
] = index
;
2350 auto const& prop
= curPropMap
[slot
];
2352 3, " index={}: slot={}, prop={}, count={}, countable={}\n",
2353 index
, slot
, prop
.name
->data(),
2354 PropertyProfile::getCount(prop
.cls
->name(), prop
.name
),
2355 jit::irgen::propertyMayBeCountable(prop
)
2357 if (serverMode
&& RuntimeOption::ServerLogReorderProps
) {
2359 " index={}: slot={}, prop={}, count={}, countable={}",
2360 index
, slot
, prop
.name
->data(),
2361 PropertyProfile::getCount(prop
.cls
->name(), prop
.name
),
2362 jit::irgen::propertyMayBeCountable(prop
)
2366 sortOwnPropsInitVec(first
, past
, slotIndex
);
2369 void Class::sortOwnPropsInitVec(uint32_t first
, uint32_t past
,
2370 const std::vector
<uint16_t>& slotIndex
) {
2371 auto const size
= past
- first
;
2372 std::vector
<TypedValue
> tmpPropInitVec(size
);
2373 std::vector
<bool> tmpDeepInit(size
);
2374 for (uint32_t i
= first
; i
< past
; i
++) {
2375 tvCopy(m_declPropInit
[i
].val
.tv(), tmpPropInitVec
[i
- first
]);
2376 tmpDeepInit
[i
- first
] = m_declPropInit
[i
].deepInit
;
2378 for (uint32_t slot
= first
; slot
< past
; slot
++) {
2379 auto index
= slotIndex
[slot
];
2380 FTRACE(3, " - placing propInit for slot {} at index {}\n", slot
, index
);
2381 tvCopy(tmpPropInitVec
[slot
- first
], m_declPropInit
[index
].val
);
2382 m_declPropInit
[index
].deepInit
= tmpDeepInit
[slot
- first
];
2386 void Class::setProperties() {
2387 int numInaccessible
= 0;
2388 PropMap::Builder curPropMap
;
2389 SPropMap::Builder curSPropMap
;
2390 m_hasDeepInitProps
= false;
2391 std::vector
<uint16_t> slotIndex
;
2393 if (m_parent
.get() != nullptr) {
2394 // m_hasDeepInitProps indicates if there are properties that require
2395 // deep initialization. Note there are cases where m_hasDeepInitProps is
2396 // true but none of the properties require deep initialization; this can
2397 // happen if a derived class redeclares a public or protected property
2398 // from an ancestor class. We still get correct behavior in these cases,
2399 // so it works out okay.
2400 m_hasDeepInitProps
= m_parent
->m_hasDeepInitProps
;
2401 for (auto const& parentProp
: m_parent
->declProperties()) {
2402 // Copy parent's declared property. Protected properties may be
2403 // weakened to public below, but otherwise, the parent's properties
2404 // will stay the same for this class.
2406 prop
.preProp
= parentProp
.preProp
;
2407 prop
.cls
= parentProp
.cls
;
2408 prop
.baseCls
= parentProp
.baseCls
;
2409 prop
.mangledName
= parentProp
.mangledName
;
2410 prop
.attrs
= parentProp
.attrs
| AttrNoBadRedeclare
;
2411 prop
.typeConstraint
= parentProp
.typeConstraint
;
2412 prop
.ubs
= parentProp
.ubs
;
2413 prop
.name
= parentProp
.name
;
2414 prop
.repoAuthType
= parentProp
.repoAuthType
;
2416 if (!(parentProp
.attrs
& AttrPrivate
)) {
2417 curPropMap
.add(prop
.name
, prop
);
2420 curPropMap
.addUnnamed(prop
);
2423 m_declPropInit
= m_parent
->m_declPropInit
;
2424 m_needsPropInitialCheck
= m_parent
->m_needsPropInitialCheck
;
2425 auto& parentSlotIndex
= m_parent
->m_slotIndex
;
2426 slotIndex
.insert(slotIndex
.end(),
2427 parentSlotIndex
.begin(), parentSlotIndex
.end());
2428 for (auto const& parentProp
: m_parent
->staticProperties()) {
2429 if ((parentProp
.attrs
& AttrPrivate
) &&
2430 !(parentProp
.attrs
& AttrLSB
)) continue;
2432 // Alias parent's static property.
2434 sProp
.preProp
= parentProp
.preProp
;
2435 sProp
.name
= parentProp
.name
;
2436 sProp
.attrs
= parentProp
.attrs
| AttrNoBadRedeclare
;
2437 sProp
.typeConstraint
= parentProp
.typeConstraint
;
2438 sProp
.ubs
= parentProp
.ubs
;
2439 sProp
.cls
= parentProp
.cls
;
2440 sProp
.repoAuthType
= parentProp
.repoAuthType
;
2441 tvWriteUninit(sProp
.val
);
2442 curSPropMap
.add(sProp
.name
, sProp
);
2446 Slot traitIdx
= m_preClass
->numProperties();
2447 if (m_preClass
->attrs() & AttrNoExpandTrait
) {
2449 (m_preClass
->properties()[traitIdx
- 1].attrs() & AttrTrait
)) {
2454 Slot serializationIdx
= 0;
2455 std::vector
<bool> serializationVisited(curPropMap
.size(), false);
2456 Slot staticSerializationIdx
= 0;
2457 std::vector
<bool> staticSerializationVisited(curSPropMap
.size(), false);
2459 static_assert(AttrPublic
< AttrProtected
&& AttrProtected
< AttrPrivate
, "");
2460 auto const firstOwnPropSlot
= curPropMap
.size();
2462 for (Slot slot
= 0; slot
< traitIdx
; ++slot
) {
2463 const PreClass::Prop
* preProp
= &m_preClass
->properties()[slot
];
2465 if (!(preProp
->attrs() & AttrStatic
)) {
2466 // Overlay/append this class's protected and public properties onto/to
2467 // those of the parent, and append this class's private properties.
2468 // Append order doesn't matter here (unlike in setMethods()).
2469 // Prohibit static-->non-static redeclaration.
2470 SPropMap::Builder::iterator it5
= curSPropMap
.find(preProp
->name());
2471 if (it5
!= curSPropMap
.end()) {
2472 raise_error("Cannot redeclare static %s::$%s as non-static %s::$%s",
2473 curSPropMap
[it5
->second
].cls
->name()->data(),
2474 preProp
->name()->data(), m_preClass
->name()->data(),
2475 preProp
->name()->data());
2477 // Get parent's equivalent property, if one exists.
2478 const Prop
* parentProp
= nullptr;
2479 if (m_parent
.get() != nullptr) {
2480 Slot id
= m_parent
->m_declProperties
.findIndex(preProp
->name());
2481 if (id
!= kInvalidSlot
) {
2482 parentProp
= &m_parent
->m_declProperties
[id
];
2485 // Prohibit strengthening.
2487 && (preProp
->attrs() & VisibilityAttrs
)
2488 > (parentProp
->attrs
& VisibilityAttrs
)) {
2490 "Access level to %s::$%s must be %s (as in class %s) or weaker",
2491 m_preClass
->name()->data(), preProp
->name()->data(),
2492 attrToVisibilityStr(parentProp
->attrs
),
2493 m_parent
->name()->data());
2495 if (preProp
->attrs() & AttrDeepInit
) {
2496 m_hasDeepInitProps
= true;
2499 auto addNewProp
= [&] {
2501 initProp(prop
, preProp
);
2503 curPropMap
.add(preProp
->name(), prop
);
2504 curPropMap
[serializationIdx
++].serializationIdx
= curPropMap
.size() - 1;
2505 serializationVisited
.push_back(true);
2506 m_declPropInit
.push_back(preProp
->val());
2507 slotIndex
.push_back(slotIndex
.size());
2510 auto const lateInitCheck
= [&] (const Class::Prop
& prop
) {
2511 if ((prop
.attrs
^ preProp
->attrs()) & AttrLateInit
) {
2513 "Property %s::$%s must %sbe <<__LateInit>> (as in class %s)",
2514 m_preClass
->name()->data(),
2515 preProp
->name()->data(),
2516 prop
.attrs
& AttrLateInit
? "" : "not ",
2517 m_parent
->name()->data()
2522 auto const redeclareProp
= [&] (const Slot slot
) {
2523 // For duplicate property name, add the slot # defined in curPropMap,
2524 // and mark the property visited.
2525 assertx(serializationVisited
.size() > slot
);
2526 if (!serializationVisited
[slot
]) {
2527 curPropMap
[serializationIdx
++].serializationIdx
= slot
;
2528 serializationVisited
[slot
] = true;
2531 auto& prop
= curPropMap
[slot
];
2532 assertx((preProp
->attrs() & VisibilityAttrs
)
2533 <= (prop
.attrs
& VisibilityAttrs
));
2534 assertx(!(prop
.attrs
& AttrNoImplicitNullable
) ||
2535 (preProp
->attrs() & AttrNoImplicitNullable
));
2536 assertx(prop
.attrs
& AttrNoBadRedeclare
);
2538 if ((preProp
->attrs() & AttrIsConst
) != (prop
.attrs
& AttrIsConst
)) {
2539 raise_error("Cannot redeclare property %s of class %s with different "
2540 "constness in class %s", preProp
->name()->data(),
2541 m_parent
->name()->data(), m_preClass
->name()->data());
2544 lateInitCheck(prop
);
2546 prop
.preProp
= preProp
;
2548 if ((preProp
->attrs() & VisibilityAttrs
)
2549 < (prop
.attrs
& VisibilityAttrs
)) {
2550 assertx((prop
.attrs
& VisibilityAttrs
) == AttrProtected
);
2551 assertx((preProp
->attrs() & VisibilityAttrs
) == AttrPublic
);
2552 // Weaken protected property to public.
2553 prop
.mangledName
= preProp
->mangledName();
2554 prop
.attrs
= Attr(prop
.attrs
^ (AttrProtected
|AttrPublic
));
2557 auto const& tc
= preProp
->typeConstraint();
2558 if (RuntimeOption::EvalCheckPropTypeHints
> 0 &&
2559 !(preProp
->attrs() & AttrNoBadRedeclare
) &&
2560 (tc
.maybeInequivalentForProp(prop
.typeConstraint
) ||
2561 !preProp
->upperBounds().empty() ||
2562 !prop
.ubs
.empty())) {
2563 // If this property isn't obviously not redeclaring a property in
2564 // the parent, we need to check that when we initialize the class.
2565 prop
.attrs
= Attr(prop
.attrs
& ~AttrNoBadRedeclare
);
2566 m_selfMaybeRedefsPropTy
= true;
2567 m_maybeRedefsPropTy
= true;
2569 prop
.typeConstraint
= tc
;
2571 prop
.ubs
= preProp
->upperBounds();
2573 if (preProp
->attrs() & AttrNoImplicitNullable
) {
2574 prop
.attrs
|= AttrNoImplicitNullable
;
2576 attrSetter(prop
.attrs
,
2577 preProp
->attrs() & AttrSystemInitialValue
,
2578 AttrSystemInitialValue
);
2579 if (preProp
->attrs() & AttrInitialSatisfiesTC
) {
2580 prop
.attrs
|= AttrInitialSatisfiesTC
;
2583 checkPrePropVal(prop
, preProp
);
2584 auto index
= slotIndex
[slot
];
2586 tvCopy(preProp
->val(), m_declPropInit
[index
].val
);
2587 copyDeepInitAttr(preProp
, &prop
);
2590 switch (preProp
->attrs() & VisibilityAttrs
) {
2595 case AttrProtected
: {
2596 // Check whether a superclass has already declared this protected
2598 PropMap::Builder::iterator it2
= curPropMap
.find(preProp
->name());
2599 if (it2
== curPropMap
.end()) {
2602 redeclareProp(it2
->second
);
2607 // Check whether a superclass has already declared this as a
2608 // protected/public property.
2609 auto it2
= curPropMap
.find(preProp
->name());
2610 if (it2
== curPropMap
.end()) {
2613 redeclareProp(it2
->second
);
2617 default: always_assert(false);
2619 } else { // Static property.
2620 // Prohibit non-static-->static redeclaration.
2621 auto const it2
= curPropMap
.find(preProp
->name());
2622 if (it2
!= curPropMap
.end()) {
2623 auto& prop
= curPropMap
[it2
->second
];
2624 raise_error("Cannot redeclare non-static %s::$%s as static %s::$%s",
2625 prop
.cls
->name()->data(),
2626 preProp
->name()->data(),
2627 m_preClass
->name()->data(),
2628 preProp
->name()->data());
2630 // Get parent's equivalent property, if one exists.
2631 auto const it3
= curSPropMap
.find(preProp
->name());
2632 Slot sPropInd
= kInvalidSlot
;
2633 // Prohibit strengthening.
2634 if (it3
!= curSPropMap
.end()) {
2635 const SProp
& parentSProp
= curSPropMap
[it3
->second
];
2636 if ((preProp
->attrs() & VisibilityAttrs
)
2637 > (parentSProp
.attrs
& VisibilityAttrs
)) {
2639 "Access level to %s::$%s must be %s (as in class %s) or weaker",
2640 m_preClass
->name()->data(), preProp
->name()->data(),
2641 attrToVisibilityStr(parentSProp
.attrs
),
2642 m_parent
->name()->data());
2644 // Prohibit overlaying LSB static properties.
2645 if (parentSProp
.attrs
& AttrLSB
) {
2647 "Cannot redeclare LSB static %s::%s as %s::%s",
2648 parentSProp
.cls
->name()->data(),
2649 preProp
->name()->data(),
2650 m_preClass
->name()->data(),
2651 preProp
->name()->data());
2653 sPropInd
= it3
->second
;
2656 if (sPropInd
== kInvalidSlot
) {
2658 initProp(sProp
, preProp
);
2659 curSPropMap
.add(sProp
.name
, sProp
);
2661 curSPropMap
[staticSerializationIdx
++].serializationIdx
=
2662 curSPropMap
.size() - 1;
2663 staticSerializationVisited
.push_back(true);
2665 // Overlay ancestor's property.
2666 auto& sProp
= curSPropMap
[sPropInd
];
2667 initProp(sProp
, preProp
);
2669 assertx(staticSerializationVisited
.size() > sPropInd
);
2670 if (!staticSerializationVisited
[sPropInd
]) {
2671 staticSerializationVisited
[sPropInd
] = true;
2672 curSPropMap
[staticSerializationIdx
++].serializationIdx
= sPropInd
;
2678 importTraitProps(traitIdx
, curPropMap
, curSPropMap
, slotIndex
,
2679 serializationIdx
, serializationVisited
,
2680 staticSerializationIdx
, staticSerializationVisited
);
2682 auto const pastOwnPropSlot
= curPropMap
.size();
2683 sortOwnProps(curPropMap
, firstOwnPropSlot
, pastOwnPropSlot
, slotIndex
);
2684 assertx(slotIndex
.size() == curPropMap
.size());
2686 // set serialization index for parent properties at the end
2687 if (m_parent
.get() != nullptr) {
2688 for (Slot i
= 0; i
< m_parent
->declProperties().size(); ++i
) {
2689 // For non-static properties, slot is always equal to parentSlot
2690 Slot slot
= m_parent
->declProperties()[i
].serializationIdx
;
2691 assertx(serializationVisited
.size() > slot
);
2692 if (!serializationVisited
[slot
]) {
2693 curPropMap
[serializationIdx
++].serializationIdx
= slot
;
2697 for (Slot i
= 0; i
< m_parent
->staticProperties().size(); ++i
) {
2698 Slot parentSlot
= m_parent
->staticProperties()[i
].serializationIdx
;
2699 auto const& prop
= m_parent
->staticProperties()[parentSlot
];
2701 if ((prop
.attrs
& AttrPrivate
) && !(prop
.attrs
& AttrLSB
)) continue;
2703 auto it
= curSPropMap
.find(prop
.name
);
2704 assertx(it
!= curSPropMap
.end());
2706 Slot slot
= it
->second
;
2707 assertx(staticSerializationVisited
.size() > slot
);
2708 if (!staticSerializationVisited
[slot
]) {
2709 curSPropMap
[staticSerializationIdx
++].serializationIdx
= slot
;
2713 assertx(serializationIdx
== curPropMap
.size());
2714 assertx(staticSerializationIdx
== curSPropMap
.size());
2717 // LSB static properties that were inherited must be initialized separately.
2718 for (Slot slot
= 0; slot
< curSPropMap
.size(); ++slot
) {
2719 auto& sProp
= curSPropMap
[slot
];
2720 if ((sProp
.attrs
& AttrLSB
) && sProp
.cls
!= this) {
2721 auto const& prevSProps
= sProp
.cls
->m_staticProperties
;
2722 auto prevInd
= prevSProps
.findIndex(sProp
.name
);
2723 assertx(prevInd
!= kInvalidSlot
);
2724 sProp
.val
= prevSProps
[prevInd
].val
;
2728 m_declProperties
.create(curPropMap
);
2729 m_staticProperties
.create(curSPropMap
);
2731 std::vector
<ObjectProps::quick_index
> slotQuickIndex
;
2732 slotQuickIndex
.reserve(slotIndex
.size());
2733 for (auto i
: slotIndex
) {
2734 slotQuickIndex
.push_back(ObjectProps::quickIndex(i
));
2736 m_slotIndex
= slotQuickIndex
;
2738 if (unsigned n
= numStaticProperties()) {
2739 using LinkT
= std::remove_pointer
<decltype(m_sPropCache
)>::type
;
2740 m_sPropCache
= static_cast<LinkT
*>(vm_malloc(n
* sizeof(LinkT
)));
2741 for (unsigned i
= 0; i
< n
; ++i
) {
2742 new (&m_sPropCache
[i
]) LinkT
;
2746 m_declPropNumAccessible
= m_declProperties
.size() - numInaccessible
;
2748 // To speed up ObjectData::release, we only iterate over property indices
2749 // up to the last countable property index. Here, "index" refers to the
2750 // position of the property in memory, and "slot" to its logical slot.
2751 uint16_t countablePropsEnd
= 0;
2752 for (Slot slot
= 0; slot
< m_declProperties
.size(); ++slot
) {
2753 if (jit::irgen::propertyMayBeCountable(m_declProperties
[slot
])) {
2755 safe_cast
<uint16_t>(propSlotToIndex(slot
) + uint16_t{1});
2756 countablePropsEnd
= std::max(countablePropsEnd
, index
);
2760 assertx(m_declProperties
.size() <= ObjectProps::max_index
+ 1);
2761 assertx(countablePropsEnd
<= ObjectProps::max_index
);
2763 m_countablePropsEnd
= ObjectProps::quickIndex(countablePropsEnd
);
2764 FTRACE(3, "numDeclProperties = {}\n", m_declProperties
.size());
2765 FTRACE(3, "countablePropsEnd = {}\n", countablePropsEnd
);
2768 bool Class::compatibleTraitPropInit(const TypedValue
& tv1
,
2769 const TypedValue
& tv2
) {
2770 if (tv1
.m_type
!= tv2
.m_type
) return false;
2772 switch (tv1
.m_type
) {
2779 case KindOfPersistentString
:
2781 return same(tvAsCVarRef(&tv1
), tvAsCVarRef(&tv2
));
2784 case KindOfPersistentVec
:
2786 case KindOfPersistentDict
:
2788 case KindOfPersistentKeyset
:
2790 case KindOfPersistentDArray
:
2792 case KindOfPersistentVArray
:
2795 case KindOfResource
:
2799 case KindOfLazyClass
:
2801 case KindOfRClsMeth
:
2810 const constexpr Attr kRedeclarePropAttrMask
=
2812 ~(AttrNoBadRedeclare
|
2813 AttrSystemInitialValue
|
2814 AttrNoImplicitNullable
|
2815 AttrInitialSatisfiesTC
|
2821 void Class::importTraitInstanceProp(Prop
& traitProp
,
2822 TypedValue traitPropVal
,
2823 PropMap::Builder
& curPropMap
,
2824 SPropMap::Builder
& curSPropMap
,
2825 std::vector
<uint16_t>& slotIndex
,
2826 Slot
& serializationIdx
,
2827 std::vector
<bool>& serializationVisited
) {
2828 // Check if prop already declared as static
2829 if (curSPropMap
.find(traitProp
.name
) != curSPropMap
.end()) {
2830 raise_error("trait declaration of property '%s' is incompatible with "
2831 "previous declaration", traitProp
.name
->data());
2834 if ((attrs() & AttrIsConst
) && !(traitProp
.attrs
& AttrIsConst
)) {
2835 raise_error("trait's non-const declaration of property '%s' is "
2836 "incompatible with a const class", traitProp
.name
->data());
2839 auto prevIt
= curPropMap
.find(traitProp
.name
);
2841 if (prevIt
== curPropMap
.end()) {
2842 // New prop, go ahead and add it
2843 auto prop
= traitProp
;
2844 prop
.baseCls
= this;
2845 // private props' mangled names contain the class name, so regenerate them
2846 if (prop
.attrs
& AttrPrivate
) {
2847 prop
.mangledName
= PreClass::manglePropName(m_preClass
->name(),
2851 if (prop
.attrs
& AttrDeepInit
) {
2852 m_hasDeepInitProps
= true;
2854 if (traitProp
.cls
!= this) {
2855 // this was a non-flattened trait property.
2858 // Clear NoImplicitNullable on the property. HHBBC analyzed the property
2859 // in the context of the trait, not this class, so we cannot predict what
2860 // derived class' will do with it. Be conservative.
2862 prop
.attrs
& ~AttrNoImplicitNullable
) | AttrNoBadRedeclare
;
2864 assertx(prop
.attrs
& AttrNoBadRedeclare
);
2867 curPropMap
.add(prop
.name
, prop
);
2868 curPropMap
[serializationIdx
++].serializationIdx
= curPropMap
.size() - 1;
2869 serializationVisited
.push_back(true);
2870 slotIndex
.push_back(slotIndex
.size());
2871 m_declPropInit
.push_back(traitPropVal
);
2873 // Redeclared prop, make sure it matches previous declarations
2874 auto& prevProp
= curPropMap
[prevIt
->second
];
2875 auto prevPropIndex
= slotIndex
[prevIt
->second
];
2876 auto const prevPropVal
= m_declPropInit
[prevPropIndex
].val
.tv();
2877 if (((prevProp
.attrs
^ traitProp
.attrs
) & kRedeclarePropAttrMask
) ||
2878 (!(prevProp
.attrs
& AttrSystemInitialValue
) &&
2879 !(traitProp
.attrs
& AttrSystemInitialValue
) &&
2880 !compatibleTraitPropInit(prevPropVal
, traitPropVal
))) {
2881 raise_error("trait declaration of property '%s' is incompatible with "
2882 "previous declaration", traitProp
.name
->data());
2885 assertx(serializationVisited
.size() > prevIt
->second
);
2886 if (!serializationVisited
[prevIt
->second
]) {
2887 curPropMap
[serializationIdx
++].serializationIdx
= prevIt
->second
;
2888 serializationVisited
[prevIt
->second
] = true;
2893 void Class::importTraitStaticProp(
2895 PropMap::Builder
& curPropMap
,
2896 SPropMap::Builder
& curSPropMap
,
2897 Slot
& staticSerializationIdx
,
2898 std::vector
<bool>& staticSerializationVisited
) {
2899 // Check if prop already declared as non-static
2900 if (curPropMap
.find(traitProp
.name
) != curPropMap
.end()) {
2901 raise_error("trait declaration of property '%s' is incompatible with "
2902 "previous declaration", traitProp
.name
->data());
2905 auto prevIt
= curSPropMap
.find(traitProp
.name
);
2906 if (prevIt
== curSPropMap
.end()) {
2907 // New prop, go ahead and add it
2908 auto prop
= traitProp
;
2909 prop
.cls
= this; // set current class as the first declaring prop
2910 prop
.attrs
|= AttrNoBadRedeclare
;
2912 curSPropMap
.add(prop
.name
, prop
);
2913 curSPropMap
[staticSerializationIdx
++].serializationIdx
=
2914 curSPropMap
.size() - 1;
2915 staticSerializationVisited
.push_back(true);
2917 // Redeclared prop, make sure it matches previous declaration
2918 auto& prevProp
= curSPropMap
[prevIt
->second
];
2919 TypedValue prevPropVal
;
2920 if (prevProp
.attrs
& AttrLSB
) {
2921 raise_error("trait declaration of property '%s' would redeclare "
2922 "LSB static", traitProp
.name
->data());
2924 if (prevProp
.cls
== this) {
2925 // If this static property was declared by this class, we can get the
2926 // initial value directly from its value.
2927 prevPropVal
= prevProp
.val
;
2929 // If this static property was declared in a parent class, its value will
2930 // be KindOfUninit, and we'll need to consult the appropriate parent class
2931 // to get the initial value.
2932 auto const& prevSProps
= prevProp
.cls
->m_staticProperties
;
2934 auto prevPropInd
= prevSProps
.findIndex(prevProp
.name
);
2935 assertx(prevPropInd
!= kInvalidSlot
);
2937 prevPropVal
= prevSProps
[prevPropInd
].val
;
2939 if (((prevProp
.attrs
^ traitProp
.attrs
) & kRedeclarePropAttrMask
) ||
2940 (!(prevProp
.attrs
& AttrSystemInitialValue
) &&
2941 !(traitProp
.attrs
& AttrSystemInitialValue
) &&
2942 !compatibleTraitPropInit(traitProp
.val
, prevPropVal
))) {
2943 raise_error("trait declaration of property '%s' is incompatible with "
2944 "previous declaration", traitProp
.name
->data());
2946 prevProp
.cls
= this;
2947 prevProp
.val
= prevPropVal
;
2949 assertx(staticSerializationVisited
.size() > prevIt
->second
);
2950 if (!staticSerializationVisited
[prevIt
->second
]) {
2951 curSPropMap
[staticSerializationIdx
++].serializationIdx
= prevIt
->second
;
2952 staticSerializationVisited
[prevIt
->second
] = true;
2957 template<typename XProp
>
2958 void Class::checkPrePropVal(XProp
& prop
, const PreClass::Prop
* preProp
) {
2959 auto const& tv
= preProp
->val();
2960 auto const& tc
= preProp
->typeConstraint();
2963 !(preProp
->attrs() & AttrSystemInitialValue
) ||
2964 tv
.m_type
!= KindOfNull
||
2965 !(preProp
->attrs() & AttrNoImplicitNullable
)
2968 auto const alwaysPassesAll
= [&] {
2969 if (!tc
.alwaysPasses(&tv
)) return false;
2970 if (RuntimeOption::EvalEnforceGenericsUB
> 0) {
2971 auto& ubs
= const_cast<PreClass::UpperBoundVec
&>(preProp
->upperBounds());
2972 for (auto& ub
: ubs
) {
2973 if (!ub
.alwaysPasses(&tv
)) return false;
2979 if (RuntimeOption::EvalCheckPropTypeHints
> 0 &&
2980 !(preProp
->attrs() & AttrInitialSatisfiesTC
) &&
2981 tv
.m_type
!= KindOfUninit
) {
2982 // System provided initial values should always be correct
2983 if ((preProp
->attrs() & AttrSystemInitialValue
) || alwaysPassesAll()) {
2984 prop
.attrs
|= AttrInitialSatisfiesTC
;
2985 } else if (preProp
->attrs() & AttrStatic
) {
2986 prop
.attrs
= Attr(prop
.attrs
& ~AttrPersistent
);
2988 m_needsPropInitialCheck
= true;
2993 template<typename XProp
>
2994 void Class::initProp(XProp
& prop
, const PreClass::Prop
* preProp
) {
2995 prop
.preProp
= preProp
;
2996 prop
.name
= preProp
->name();
2997 prop
.attrs
= Attr(preProp
->attrs() & ~AttrTrait
) |
2999 // This is the first class to declare this property
3001 prop
.typeConstraint
= preProp
->typeConstraint();
3002 prop
.ubs
= preProp
->upperBounds();
3003 prop
.repoAuthType
= preProp
->repoAuthType();
3005 // If type constraint has soft or nullable flags, apply them to upper-bounds.
3006 for (auto &ub
: prop
.ubs
) applyFlagsToUB(ub
, prop
.typeConstraint
);
3007 // Check if this property's initial value needs to be type checked at
3009 checkPrePropVal(prop
, preProp
);
3012 void Class::initProp(Prop
& prop
, const PreClass::Prop
* preProp
) {
3013 initProp
<Prop
>(prop
, preProp
);
3014 prop
.mangledName
= preProp
->mangledName();
3015 prop
.baseCls
= this;
3018 void Class::initProp(SProp
& prop
, const PreClass::Prop
* preProp
) {
3019 initProp
<SProp
>(prop
, preProp
);
3020 prop
.val
= preProp
->val();
3023 void Class::importTraitProps(int traitIdx
,
3024 PropMap::Builder
& curPropMap
,
3025 SPropMap::Builder
& curSPropMap
,
3026 std::vector
<uint16_t>& slotIndex
,
3027 Slot
& serializationIdx
,
3028 std::vector
<bool>& serializationVisited
,
3029 Slot
& staticSerializationIdx
,
3030 std::vector
<bool>& staticSerializationVisited
) {
3031 if (attrs() & AttrNoExpandTrait
) {
3032 for (Slot p
= traitIdx
; p
< m_preClass
->numProperties(); p
++) {
3033 auto const* preProp
= &m_preClass
->properties()[p
];
3034 assertx(preProp
->attrs() & AttrTrait
);
3035 if (!(preProp
->attrs() & AttrStatic
)) {
3037 initProp(prop
, preProp
);
3038 importTraitInstanceProp(prop
, preProp
->val(),
3039 curPropMap
, curSPropMap
, slotIndex
,
3040 serializationIdx
, serializationVisited
);
3043 initProp(prop
, preProp
);
3044 importTraitStaticProp(
3045 prop
, curPropMap
, curSPropMap
,
3046 staticSerializationIdx
, staticSerializationVisited
);
3052 for (auto const& t
: m_extra
->m_usedTraits
) {
3053 auto trait
= t
.get();
3055 m_needsPropInitialCheck
|= trait
->m_needsPropInitialCheck
;
3057 // instance properties
3058 for (Slot p
= 0; p
< trait
->m_declProperties
.size(); p
++) {
3059 auto& traitProp
= trait
->m_declProperties
[p
];
3060 auto traitPropIndex
= trait
->propSlotToIndex(p
);
3061 auto traitPropVal
= trait
->m_declPropInit
[traitPropIndex
].val
.tv();
3062 importTraitInstanceProp(traitProp
, traitPropVal
,
3063 curPropMap
, curSPropMap
, slotIndex
,
3064 serializationIdx
, serializationVisited
);
3067 // static properties
3068 for (Slot p
= 0; p
< trait
->m_staticProperties
.size(); ++p
) {
3069 auto& traitProp
= trait
->m_staticProperties
[p
];
3070 importTraitStaticProp(
3071 traitProp
, curPropMap
, curSPropMap
,
3072 staticSerializationIdx
, staticSerializationVisited
);
3077 void Class::addTraitPropInitializers(std::vector
<const Func
*>& thisInitVec
,
3079 if (attrs() & AttrNoExpandTrait
) return;
3080 auto getInitVec
= [&](Class
* trait
) -> auto& {
3082 case AttrNone
: return trait
->m_pinitVec
;
3083 case AttrStatic
: return trait
->m_sinitVec
;
3084 case AttrLSB
: return trait
->m_linitVec
;
3085 default: always_assert(false);
3088 for (auto const& t
: m_extra
->m_usedTraits
) {
3089 Class
* trait
= t
.get();
3090 auto& traitInitVec
= getInitVec(trait
);
3091 // Insert trait's 86[psl]init into the current class, avoiding repetitions.
3092 for (unsigned m
= 0; m
< traitInitVec
.size(); m
++) {
3093 // Clone 86[psl]init methods, and set the class to the current class.
3094 // This allows 86[psl]init to determine the property offset for the
3095 // initializer array corectly.
3096 Func
*f
= traitInitVec
[m
]->clone(this);
3098 f
->setBaseCls(this);
3099 f
->setHasPrivateAncestor(false);
3100 thisInitVec
.push_back(f
);
3105 void Class::setReifiedData() {
3106 auto const ua
= m_preClass
->userAttributes();
3107 auto const it
= ua
.find(s___Reified
.get());
3108 if (it
!= ua
.end()) m_hasReifiedGenerics
= true;
3109 if (m_parent
.get()) {
3110 m_hasReifiedParent
=
3111 m_parent
->m_hasReifiedGenerics
|| m_parent
->m_hasReifiedParent
;
3113 if (m_hasReifiedGenerics
) {
3114 auto tv
= it
->second
;
3115 assertx(tvIsHAMSafeVArray(tv
));
3117 m_extra
.raw()->m_reifiedGenericsInfo
=
3118 extractSizeAndPosFromReifiedAttribute(tv
.m_data
.parr
);
3123 const StaticString
s_Error("Error");
3124 const StaticString
s_Exception("Exception");
3127 void Class::setInitializers() {
3128 std::vector
<const Func
*> pinits
;
3129 std::vector
<const Func
*> sinits
;
3130 std::vector
<const Func
*> linits
;
3132 if (m_parent
.get() != nullptr) {
3133 // Copy parent's 86pinit() vector, so that the 86pinit() methods can be
3134 // called in reverse order without any search/recursion during
3136 pinits
.assign(m_parent
->m_pinitVec
.begin(), m_parent
->m_pinitVec
.end());
3138 // Copy parent's 86linit into the current class, and set class to the
3140 for (const auto& linit
: m_parent
->m_linitVec
) {
3141 Func
*f
= linit
->clone(this);
3143 f
->setBaseCls(this);
3144 f
->setHasPrivateAncestor(false);
3145 linits
.push_back(f
);
3149 // Clone 86pinit methods from traits
3150 addTraitPropInitializers(pinits
, AttrNone
);
3152 // If this class has an 86pinit method, append it last so that
3153 // reverse iteration of the vector runs this class's 86pinit
3154 // first, in case multiple classes in the hierarchy initialize
3155 // the same property.
3156 const Func
* meth86pinit
= findSpecialMethod(this, s_86pinit
.get());
3157 if (meth86pinit
!= nullptr) {
3158 pinits
.push_back(meth86pinit
);
3161 // If this class has an 86sinit method, it must be the last element
3162 // in the vector. See get86sinit().
3163 addTraitPropInitializers(sinits
, AttrStatic
);
3164 const Func
* sinit
= findSpecialMethod(this, s_86sinit
.get());
3166 sinits
.push_back(sinit
);
3169 addTraitPropInitializers(linits
, AttrLSB
);
3170 const Func
* meth86linit
= findSpecialMethod(this, s_86linit
.get());
3171 if (meth86linit
!= nullptr) {
3172 linits
.push_back(meth86linit
);
3175 m_pinitVec
= pinits
;
3176 m_sinitVec
= sinits
;
3177 m_linitVec
= linits
;
3179 m_needInitialization
=
3180 (m_pinitVec
.size() > 0 ||
3181 m_staticProperties
.size() > 0 ||
3182 m_maybeRedefsPropTy
||
3183 m_needsPropInitialCheck
);
3185 if (m_maybeRedefsPropTy
|| m_needsPropInitialCheck
) allocExtraData();
3187 // Implementations of Throwable get special treatment.
3188 if (m_parent
.get() != nullptr) {
3189 m_needsInitThrowable
= m_parent
->needsInitThrowable();
3191 m_needsInitThrowable
= name()->same(s_Exception
.get()) ||
3192 name()->same(s_Error
.get());
3196 const StaticString
s_HH_Iterator("HH\\Iterator");
3197 const StaticString
s_IteratorAggregate("IteratorAggregate");
3198 void Class::checkInterfaceConstraints() {
3199 if (UNLIKELY(m_interfaces
.contains(s_HH_Iterator
.get()) &&
3200 m_interfaces
.contains(s_IteratorAggregate
.get()))) {
3201 raise_error("Class %s cannot implement both IteratorAggregate and Iterator"
3202 " at the same time", name()->data());
3206 // Checks if interface methods are OK:
3207 // - there's no requirement if this is a trait, interface, or abstract class
3208 // - a non-abstract class must implement all methods from interfaces it
3209 // declares to implement (either directly or indirectly), arity must be
3210 // compatible (at least as many parameters, additional parameters must have
3211 // defaults), and typehints must be compatible
3212 void Class::checkInterfaceMethods() {
3213 for (int i
= 0, size
= m_interfaces
.size(); i
< size
; i
++) {
3214 const Class
* iface
= m_interfaces
[i
];
3216 for (size_t m
= 0; m
< iface
->m_methods
.size(); m
++) {
3217 Func
* imeth
= iface
->getMethod(m
);
3218 const StringData
* methName
= imeth
->name();
3220 // Skip special methods
3221 if (Func::isSpecial(methName
)) continue;
3223 Func
* meth
= lookupMethod(methName
);
3225 if (attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
)) {
3226 if (meth
== nullptr) {
3227 // Skip unimplemented method.
3231 // Verify that method is not abstract within concrete class.
3232 if (meth
== nullptr || (meth
->attrs() & AttrAbstract
)) {
3233 raise_error("Class %s contains abstract method (%s) and "
3234 "must therefore be declared abstract or implement "
3235 "the remaining methods", name()->data(),
3239 bool ifaceStaticMethod
= imeth
->attrs() & AttrStatic
;
3240 bool classStaticMethod
= meth
->attrs() & AttrStatic
;
3241 if (classStaticMethod
!= ifaceStaticMethod
) {
3242 raise_error("Cannot make %sstatic method %s::%s() %sstatic "
3244 ifaceStaticMethod
? "" : "non-",
3245 iface
->m_preClass
->name()->data(), methName
->data(),
3246 classStaticMethod
? "" : "non-",
3247 m_preClass
->name()->data());
3249 if ((imeth
->attrs() & AttrPublic
) &&
3250 !(meth
->attrs() & AttrPublic
)) {
3251 raise_error("Access level to %s::%s() must be public "
3252 "(as in interface %s)", m_preClass
->name()->data(),
3253 methName
->data(), iface
->m_preClass
->name()->data());
3255 checkDeclarationCompat(m_preClass
.get(), meth
, imeth
);
3261 * Look up the interfaces implemented by traits used by the class, and add them
3262 * to the provided builder.
3264 void Class::addInterfacesFromUsedTraits(InterfaceMap::Builder
& builder
) const {
3266 for (auto const& traitName
: m_preClass
->usedTraits()) {
3267 auto const trait
= Unit::lookupClass(traitName
);
3268 assertx(trait
->attrs() & AttrTrait
);
3269 int numIfcs
= trait
->m_interfaces
.size();
3271 for (int i
= 0; i
< numIfcs
; i
++) {
3272 auto interface
= trait
->m_interfaces
[i
];
3273 if (!builder
.contains(interface
->name())) {
3274 builder
.add(interface
->name(), interface
);
3280 const StaticString
s_Stringish("Stringish");
3281 const StaticString
s_XHPChild("XHPChild");
3283 void Class::setInterfaces() {
3284 InterfaceMap::Builder interfacesBuilder
;
3285 if (m_parent
.get() != nullptr) {
3286 int size
= m_parent
->m_interfaces
.size();
3287 for (int i
= 0; i
< size
; i
++) {
3288 auto interface
= m_parent
->m_interfaces
[i
];
3289 interfacesBuilder
.add(interface
->name(), interface
);
3293 std::vector
<ClassPtr
> declInterfaces
;
3295 for (auto it
= m_preClass
->interfaces().begin();
3296 it
!= m_preClass
->interfaces().end(); ++it
) {
3297 auto cp
= Unit::loadClass(*it
);
3298 if (cp
== nullptr) {
3299 raise_error("Undefined interface: %s", (*it
)->data());
3301 if (!(cp
->attrs() & AttrInterface
)) {
3302 raise_error("%s cannot implement %s - it is not an interface",
3303 m_preClass
->name()->data(), cp
->name()->data());
3305 m_preClass
->enforceInMaybeSealedParentWhitelist(cp
->preClass());
3306 declInterfaces
.push_back(ClassPtr(cp
));
3307 if (!interfacesBuilder
.contains(cp
->name())) {
3308 interfacesBuilder
.add(cp
->name(), LowPtr
<Class
>(cp
));
3310 int size
= cp
->m_interfaces
.size();
3311 for (int i
= 0; i
< size
; i
++) {
3312 auto interface
= cp
->m_interfaces
[i
];
3313 if (!interfacesBuilder
.contains(interface
->name())) {
3314 interfacesBuilder
.add(interface
->name(), interface
);
3319 if (auto sz
= declInterfaces
.size()) {
3320 m_declInterfaces
.reserve(sz
);
3321 for (auto interface
: declInterfaces
) {
3322 m_declInterfaces
.push_back(interface
);
3326 addInterfacesFromUsedTraits(interfacesBuilder
);
3329 if (!interfacesBuilder
.contains(s_Stringish
.get()) &&
3330 (!(attrs() & AttrInterface
) ||
3331 !m_preClass
->name()->isame(s_Stringish
.get()))) {
3333 Class
* stringish
= Unit::lookupClass(s_Stringish
.get());
3334 assertx(stringish
!= nullptr);
3335 assertx((stringish
->attrs() & AttrInterface
));
3336 interfacesBuilder
.add(stringish
->name(), LowPtr
<Class
>(stringish
));
3338 if (!m_preClass
->name()->isame(s_XHPChild
.get()) &&
3339 !interfacesBuilder
.contains(s_XHPChild
.get())) {
3340 // All Stringish are also XHPChild
3341 Class
* xhpChild
= Unit::lookupClass(s_XHPChild
.get());
3342 assertx(xhpChild
!= nullptr);
3343 assertx((xhpChild
->attrs() & AttrInterface
));
3344 interfacesBuilder
.add(xhpChild
->name(), LowPtr
<Class
>(xhpChild
));
3349 m_interfaces
.create(interfacesBuilder
);
3350 checkInterfaceConstraints();
3351 checkInterfaceMethods();
3354 void Class::setInterfaceVtables() {
3355 // We only need to set interface vtables for classes that can be instantiated
3356 // and implement more than 0 interfaces.
3357 if (!RuntimeOption::RepoAuthoritative
||
3358 !isNormalClass(this) || isAbstract(this) || m_interfaces
.empty()) return;
3360 size_t totalMethods
= 0;
3362 for (auto iface
: m_interfaces
.range()) {
3363 auto const slot
= iface
->preClass()->ifaceVtableSlot();
3364 if (slot
== kInvalidSlot
) continue;
3366 maxSlot
= std::max(maxSlot
, slot
);
3367 totalMethods
+= iface
->numMethods();
3370 const size_t nVtables
= maxSlot
+ 1;
3371 auto const vtableVecSz
= nVtables
* sizeof(VtableVecSlot
);
3372 auto const memSz
= vtableVecSz
+ totalMethods
* sizeof(LowPtr
<Func
>);
3373 auto const mem
= static_cast<char*>(low_malloc(memSz
));
3376 ITRACE(3, "Setting interface vtables for class {}. "
3377 "{} interfaces, {} vtable slots, {} total methods\n",
3378 name()->data(), m_interfaces
.size(), nVtables
, totalMethods
);
3379 Trace::Indent indent
;
3381 auto const vtableVec
= reinterpret_cast<VtableVecSlot
*>(cursor
);
3382 cursor
+= vtableVecSz
;
3383 m_vtableVecLen
= always_safe_cast
<decltype(m_vtableVecLen
)>(nVtables
);
3384 m_vtableVec
= vtableVec
;
3385 memset(vtableVec
, 0, vtableVecSz
);
3387 for (auto iface
: m_interfaces
.range()) {
3388 auto const slot
= iface
->preClass()->ifaceVtableSlot();
3389 if (slot
== kInvalidSlot
) continue;
3390 ITRACE(3, "{} @ slot {}\n", iface
->name()->data(), slot
);
3391 Trace::Indent indent2
;
3392 always_assert(slot
< nVtables
);
3394 auto const nMethods
= iface
->numMethods();
3395 auto const vtable
= reinterpret_cast<LowPtr
<Func
>*>(cursor
);
3396 cursor
+= nMethods
* sizeof(LowPtr
<Func
>);
3397 if (vtableVec
[slot
].vtable
!= nullptr) {
3398 raise_error("Static analysis failure: "
3399 "%s was expected to fatal at runtime, but didn't",
3400 m_preClass
->name()->data());
3402 vtableVec
[slot
].vtable
= vtable
;
3403 vtableVec
[slot
].iface
= iface
;
3405 for (size_t i
= 0; i
< nMethods
; ++i
) {
3406 auto ifunc
= iface
->getMethod(i
);
3407 auto func
= lookupMethod(ifunc
->name());
3408 ITRACE(3, "{}:{} @ slot {}\n", ifunc
->name()->data(), func
, i
);
3409 always_assert(func
|| Func::isSpecial(ifunc
->name()));
3414 always_assert(cursor
== mem
+ memSz
);
3417 void Class::setRequirements() {
3418 RequirementMap::Builder reqBuilder
;
3420 auto addReq
= [&](const PreClass::ClassRequirement
* req
) {
3421 auto it
= reqBuilder
.find(req
->name());
3422 if (it
== reqBuilder
.end()) {
3423 reqBuilder
.add(req
->name(), req
);
3426 if (!reqBuilder
[it
->second
]->is_same(req
)) {
3427 raise_error("Conflicting requirements for '%s'",
3428 req
->name()->data());
3432 if (m_parent
.get() != nullptr) {
3433 for (auto const& req
: m_parent
->allRequirements().range()) {
3434 // no need for addReq; parent won't have duplicates
3435 reqBuilder
.add(req
->name(), req
);
3438 for (auto const& iface
: m_interfaces
.range()) {
3439 for (auto const& req
: iface
->allRequirements().range()) {
3443 for (auto const& ut
: m_extra
->m_usedTraits
) {
3444 for (auto const& req
: ut
->allRequirements().range()) {
3449 if (attrs() & AttrInterface
) {
3450 // Check that requirements are semantically valid
3451 // We don't require the class to exist yet, to avoid creating
3452 // pointless circular dependencies, but if it does, we check
3453 // that it's the right kind.
3454 for (auto const& req
: m_preClass
->requirements()) {
3455 assertx(req
.is_extends());
3456 auto const reqName
= req
.name();
3457 auto const reqCls
= Unit::lookupClass(reqName
);
3459 if (reqCls
->attrs() & (AttrTrait
| AttrInterface
| AttrFinal
)) {
3460 raise_error("Interface '%s' requires extension of '%s', but %s "
3461 "is not an extendable class",
3462 m_preClass
->name()->data(),
3469 } else if (attrs() & (AttrTrait
| AttrNoExpandTrait
)) {
3470 // Check that requirements are semantically valid.
3471 // Note that trait flattening could have migrated
3472 // requirements onto a class's preClass.
3473 for (auto const& req
: m_preClass
->requirements()) {
3474 auto const reqName
= req
.name();
3475 if (auto const reqCls
= Unit::lookupClass(reqName
)) {
3476 if (req
.is_extends()) {
3477 if (reqCls
->attrs() & (AttrTrait
| AttrInterface
| AttrFinal
)) {
3478 raise_error(Strings::TRAIT_BAD_REQ_EXTENDS
,
3479 m_preClass
->name()->data(),
3484 assertx(req
.is_implements());
3485 if (!(reqCls
->attrs() & AttrInterface
)) {
3486 raise_error(Strings::TRAIT_BAD_REQ_IMPLEMENTS
,
3487 m_preClass
->name()->data(),
3497 m_requirements
.create(reqBuilder
);
3498 checkRequirementConstraints();
3501 void Class::setEnumType() {
3502 if (attrs() & AttrEnum
) {
3503 m_enumBaseTy
= m_preClass
->enumBaseTy().underlyingDataTypeResolved();
3505 // Make sure we've loaded a valid underlying type.
3507 !isIntType(*m_enumBaseTy
) &&
3508 !isStringType(*m_enumBaseTy
)) {
3509 raise_error("Invalid base type for enum %s",
3510 m_preClass
->name()->data());
3515 void Class::setLSBMemoCacheInfo() {
3516 boost::container::flat_map
<FuncId
, Slot
> slots
;
3519 /* Inherit slots from parent */
3520 if (m_parent
&& m_parent
->m_extra
) {
3521 numSlots
= m_parent
->m_extra
->m_lsbMemoExtra
.m_numSlots
;
3524 /* If any of our methods need slots, insert them at the end */
3525 auto const methodCount
= numMethods();
3526 uint64_t assignCount
= 0;
3527 for (Slot i
= 0; i
< methodCount
; ++i
) {
3528 auto const f
= getMethod(i
);
3529 if (f
->isMemoizeWrapperLSB() && f
->cls() == this) {
3530 slots
.emplace(f
->getFuncId(), numSlots
++);
3534 always_assert(assignCount
== slots
.size());
3536 if (numSlots
!= 0) {
3538 auto& mx
= m_extra
->m_lsbMemoExtra
;
3539 mx
.m_slots
= std::move(slots
);
3540 mx
.m_numSlots
= numSlots
;
3541 initLSBMemoHandles();
3545 void Class::initLSBMemoHandles() {
3546 /* Allocate handles array */
3547 auto& mx
= m_extra
->m_lsbMemoExtra
;
3548 auto const numSlots
= mx
.m_numSlots
;
3549 assertx(numSlots
> 0);
3550 rds::Handle
* handles
= static_cast<rds::Handle
*>(
3551 vm_malloc(numSlots
* sizeof(rds::Handle
)));
3553 /* Assign handle to every slot */
3554 uint64_t assignCount
= 0;
3555 uint64_t assignSum
= 0;
3556 const Class
* cls
= this;
3557 while (cls
!= nullptr && cls
->m_extra
) {
3558 for (const auto& kv
: cls
->m_extra
->m_lsbMemoExtra
.m_slots
) {
3559 auto const func
= Func::fromFuncId(kv
.first
);
3560 auto const slot
= kv
.second
;
3561 assertx(slot
>= 0 && slot
< numSlots
);
3562 if (func
->numParams() == 0) {
3563 handles
[slot
] = rds::bindLSBMemoValue(this, func
).handle();
3565 handles
[slot
] = rds::bindLSBMemoCache(this, func
).handle();
3570 cls
= cls
->m_parent
.get();
3572 /* Sanity check: Make sure we assigned every slot exactly once */
3573 always_assert(numSlots
== assignCount
);
3574 always_assert(assignSum
== ((numSlots
- 1) * numSlots
) / 2);
3576 assertx(mx
.m_handles
== nullptr);
3577 mx
.m_handles
= handles
;
3580 void Class::setInstanceMemoCacheInfo() {
3581 // Inherit the data from the parent, if any.
3582 if (m_parent
&& m_parent
->hasMemoSlots()) {
3584 m_extra
.raw()->m_nextMemoSlot
= m_parent
->m_extra
->m_nextMemoSlot
;
3585 m_extra
.raw()->m_sharedMemoSlots
= m_parent
->m_extra
->m_sharedMemoSlots
;
3588 // If we have memo slots defined already, and this class is adding (or
3589 // changing) native data, forbid it. The parent class won't realize the native
3590 // data has changed size, so code we generate in it to access the slot will be
3591 // incorrect (it won't find the slot in the right place). If we ever have a
3592 // need to actually do this, we'll have to revisit this.
3593 if (m_preClass
->nativeDataInfo() && m_extra
->m_nextMemoSlot
> 0) {
3595 "Class '%s' with existing memoized methods cannot have native-data added",
3596 m_preClass
->name()->data()
3600 auto const forEachMeth
= [&](auto f
) {
3601 auto const methodCount
= numMethods();
3602 for (Slot i
= 0; i
< methodCount
; ++i
) {
3603 auto const m
= getMethod(i
);
3604 if (m
->isMemoizeWrapper() && !m
->isStatic() && m
->cls() == this) f(m
);
3608 auto const addNonShared
= [&](const Func
* f
) {
3610 auto extra
= m_extra
.raw();
3611 extra
->m_memoMappings
.emplace(
3613 std::make_pair(extra
->m_nextMemoSlot
++, false)
3617 auto const addShared
= [&](const Func
* f
) {
3619 auto extra
= m_extra
.raw();
3621 // There's no point in allocating slots for parameter counts greater than
3622 // kMemoCacheMaxSpecializedKeys, since they'll all be generic caches
3623 // anyways. Cap the count at that.
3624 auto const keyCount
=
3625 std::min
<size_t>(f
->numParams(), kMemoCacheMaxSpecializedKeys
+ 1);
3626 auto const it
= extra
->m_sharedMemoSlots
.find(keyCount
);
3627 if (it
!= extra
->m_sharedMemoSlots
.end()) {
3628 extra
->m_memoMappings
.emplace(
3630 std::make_pair(it
->second
, true)
3635 extra
->m_sharedMemoSlots
.emplace(keyCount
, extra
->m_nextMemoSlot
);
3636 extra
->m_memoMappings
.emplace(
3638 std::make_pair(extra
->m_nextMemoSlot
++, true)
3642 // First count how many methods we have without parameters or with parameters.
3643 size_t methNoKeys
= 0;
3644 size_t methWithKeys
= 0;
3646 [&](const Func
* f
) {
3647 if (f
->numParams() == 0) {
3655 // We only want to assign up to EvalNonSharedInstanceMemoCaches non-shared
3656 // caches. With the remaining non-assigned slots, we give preference to the
3657 // parameter-less methods first. This is because there's a greater benefit to
3658 // giving parameter-less methods non-shared slots (they can just be a
3659 // TypedValue). However, we only give the methods non-shared slots if we can give
3660 // them to all the methods. If there's more methods than we have available
3661 // non-shared slots, its not clear which ones should get the non-shared slots
3662 // and which ones shouldn't, so we treat them all equally and give them all
3663 // shared slots. After processing the parameter-less methods, we apply the
3664 // same logic to the methods with parameters.
3666 // How many non-shared slots do we have left to allocate?
3668 RuntimeOption::EvalNonSharedInstanceMemoCaches
-
3670 m_extra
->m_nextMemoSlot
,
3671 RuntimeOption::EvalNonSharedInstanceMemoCaches
3674 if (methNoKeys
> 0) {
3676 [&](const Func
* f
) {
3677 if (f
->numParams() == 0) {
3678 (methNoKeys
<= slotsLeft
) ? addNonShared(f
) : addShared(f
);
3682 if (methNoKeys
<= slotsLeft
) slotsLeft
-= methNoKeys
;
3685 if (methWithKeys
> 0) {
3687 [&](const Func
* f
) {
3688 if (f
->numParams() > 0) {
3689 (methWithKeys
<= slotsLeft
) ? addNonShared(f
) : addShared(f
);
3697 ObjectData
* throwingInstanceCtor(Class
* cls
) {
3698 SystemLib::throwExceptionObject(
3699 folly::sformat("Class {} may not be directly instantiated", cls
->name())
3704 void Class::setNativeDataInfo() {
3705 for (auto cls
= this; cls
; cls
= cls
->parent()) {
3706 if (auto ndi
= cls
->preClass()->nativeDataInfo()) {
3708 m_extra
.raw()->m_nativeDataInfo
= ndi
;
3709 if (ndi
->ctor_throws
) {
3710 m_extra
.raw()->m_instanceCtor
= throwingInstanceCtor
;
3711 m_extra
.raw()->m_instanceCtorUnlocked
= throwingInstanceCtor
;
3713 m_extra
.raw()->m_instanceCtor
= Native::nativeDataInstanceCtor
<false>;
3714 m_extra
.raw()->m_instanceCtorUnlocked
=
3715 Native::nativeDataInstanceCtor
<true>;
3717 m_extra
.raw()->m_instanceDtor
= Native::nativeDataInstanceDtor
;
3718 m_releaseFunc
= Native::nativeDataInstanceDtor
;
3719 m_RTAttrs
|= ndi
->rt_attrs
;
3725 void Class::initClosure() {
3726 if (!isBuiltin() || !m_preClass
->name()->equal(s_Closure
.get())) return;
3728 c_Closure::cls_Closure
= this;
3729 assertx(!hasMemoSlots());
3731 auto& extraData
= *m_extra
.raw();
3732 extraData
.m_instanceCtor
= c_Closure::instanceCtor
;
3733 extraData
.m_instanceCtorUnlocked
= c_Closure::instanceCtor
;
3734 extraData
.m_instanceDtor
= c_Closure::instanceDtor
;
3735 m_releaseFunc
= c_Closure::instanceDtor
;
3738 bool Class::hasNativePropHandler() const {
3739 return m_RTAttrs
& Class::HasNativePropHandler
;
3742 const Native::NativePropHandler
* Class::getNativePropHandler() const {
3743 assertx(hasNativePropHandler());
3745 for (auto cls
= this; cls
; cls
= cls
->parent()) {
3746 auto propHandler
= Native::getNativePropHandler(cls
->name());
3747 if (propHandler
!= nullptr) {
3755 void Class::raiseUnsatisfiedRequirement(const PreClass::ClassRequirement
* req
) const {
3756 assertx(!(attrs() & (AttrInterface
| AttrTrait
)));
3758 auto const reqName
= req
->name();
3759 if (req
->is_implements()) {
3760 // "require implements" is only allowed on traits.
3762 assertx(attrs() & AttrNoExpandTrait
||
3763 (m_extra
&& m_extra
->m_usedTraits
.size() > 0));
3764 for (auto const& traitCls
: m_extra
->m_usedTraits
) {
3765 if (traitCls
->allRequirements().contains(reqName
)) {
3766 raise_error(Strings::TRAIT_REQ_IMPLEMENTS
,
3767 m_preClass
->name()->data(),
3769 traitCls
->preClass()->name()->data());
3773 if (attrs() & AttrNoExpandTrait
) {
3774 // As a result of trait flattening, the PreClass of this normal class
3775 // contains a requirement. To save space, we don't include the source
3776 // trait in the requirement. For details, see
3777 // ClassScope::importUsedTraits in the compiler.
3778 assertx(!m_extra
|| m_extra
->m_usedTraits
.size() == 0);
3779 assertx(m_preClass
->requirements().size() > 0);
3780 raise_error(Strings::TRAIT_REQ_IMPLEMENTS
,
3781 m_preClass
->name()->data(),
3786 always_assert(false); // requirements cannot spontaneously generate
3790 assertx(req
->is_extends());
3791 for (auto const& iface
: m_interfaces
.range()) {
3792 if (iface
->allRequirements().contains(reqName
)) {
3793 raise_error("Class '%s' required to extend class '%s'"
3794 " by interface '%s'",
3795 m_preClass
->name()->data(),
3797 iface
->preClass()->name()->data());
3801 for (auto const& traitCls
: m_extra
->m_usedTraits
) {
3802 if (traitCls
->allRequirements().contains(reqName
)) {
3803 raise_error(Strings::TRAIT_REQ_EXTENDS
,
3804 m_preClass
->name()->data(),
3806 traitCls
->preClass()->name()->data());
3810 if (attrs() & AttrNoExpandTrait
) {
3811 // A result of trait flattening, as with the is_implements case above
3812 assertx(!m_extra
|| m_extra
->m_usedTraits
.size() == 0);
3813 assertx(m_preClass
->requirements().size() > 0);
3814 raise_error(Strings::TRAIT_REQ_EXTENDS
,
3815 m_preClass
->name()->data(),
3820 // calls to this method are expected to come as a result of an error due
3821 // to a requirement coming from traits or interfaces
3822 always_assert(false);
3825 void Class::checkRequirementConstraints() const {
3826 if (attrs() & (AttrInterface
| AttrTrait
)) return;
3828 for (auto const& req
: m_requirements
.range()) {
3829 auto const reqName
= req
->name();
3830 if (req
->is_implements()) {
3831 if (UNLIKELY(!ifaceofDirect(reqName
))) {
3832 raiseUnsatisfiedRequirement(req
);
3835 auto reqExtCls
= Unit::lookupClass(reqName
);
3837 (reqExtCls
== nullptr) ||
3838 (reqExtCls
->attrs() & (AttrTrait
| AttrInterface
)))) {
3839 raiseUnsatisfiedRequirement(req
);
3842 if (UNLIKELY(!classofNonIFace(reqExtCls
))) {
3843 raiseUnsatisfiedRequirement(req
);
3849 void Class::setClassVec() {
3850 if (m_classVecLen
> 1) {
3851 assertx(m_parent
.get() != nullptr);
3852 memcpy(m_classVec
, m_parent
->m_classVec
,
3853 (m_classVecLen
-1) * sizeof(m_classVec
[0]));
3855 m_classVec
[m_classVecLen
-1] = this;
3858 void Class::setFuncVec(MethodMapBuilder
& builder
) {
3859 auto funcVec
= this->funcVec();
3861 memset(funcVec
, 0, m_funcVecLen
* sizeof(LowPtr
<Func
>));
3863 funcVec
= (LowPtr
<Func
>*)this;
3864 assertx(builder
.size() <= m_funcVecLen
);
3866 for (Slot i
= 0; i
< builder
.size(); i
++) {
3867 assertx(builder
[i
]->methodSlot() < builder
.size());
3868 funcVec
[-((int32_t)builder
[i
]->methodSlot() + 1)] = builder
[i
];
3872 void Class::setReleaseData() {
3873 if (getNativeDataInfo()) return;
3874 if (hasMemoSlots()) {
3875 m_memoSize
= ObjectData::objOffFromMemoNode(this);
3877 auto const nProps
= numDeclProperties();
3878 auto const size
= m_memoSize
+ ObjectData::sizeForNProps(nProps
);
3879 m_sizeIdx
= MemoryManager::size2Index(size
);
3882 void Class::getMethodNames(const Class
* cls
,
3886 // The order of these methods is so that the first ones win on
3887 // case insensitive name conflicts.
3889 auto const numMethods
= cls
->numMethods();
3891 for (Slot i
= 0; i
< numMethods
; ++i
) {
3892 auto const meth
= cls
->getMethod(i
);
3893 auto const declCls
= meth
->cls();
3894 auto addMeth
= [&]() {
3895 auto const methName
= Variant(meth
->name(), Variant::PersistentStrInit
{});
3896 auto const lowerName
= f_strtolower(methName
.toString());
3897 if (!out
.exists(lowerName
)) {
3898 out
.set(lowerName
, methName
);
3902 // Only pick methods declared in this class, in order to match
3903 // Zend's order. Inherited methods will be inserted in the
3904 // recursive call later.
3905 if (declCls
!= cls
) continue;
3907 // Skip generated, internal methods.
3908 if (meth
->isGenerated()) continue;
3910 // Public methods are always visible.
3911 if ((meth
->attrs() & AttrPublic
)) {
3916 // In anonymous contexts, only public methods are visible.
3919 // All methods are visible if the context is the class that
3920 // declared them. If the context is not the declCls, protected
3921 // methods are visible in context classes related the declCls.
3922 if (declCls
== ctx
||
3923 ((meth
->attrs() & AttrProtected
) &&
3924 (ctx
->classof(declCls
) || declCls
->classof(ctx
)))) {
3929 // Now add the inherited methods.
3930 if (auto const parent
= cls
->parent()) {
3931 getMethodNames(parent
, ctx
, out
);
3934 // Add interface methods that the class may not have implemented yet.
3935 for (auto& iface
: cls
->m_declInterfaces
) {
3936 getMethodNames(iface
.get(), ctx
, out
);
3941 ///////////////////////////////////////////////////////////////////////////////
3942 // Trait method import.
3946 struct TraitMethod
{
3947 TraitMethod(const Class
* trait_
, const Func
* method_
, Attr modifiers_
)
3950 , modifiers(modifiers_
)
3953 using class_type
= const Class
*;
3954 using method_type
= const Func
*;
3962 // Return the name for the trait class.
3963 static const StringData
* clsName(const Class
* traitCls
) {
3964 return traitCls
->name();
3967 static const StringData
* methName(const Func
* method
) {
3968 return method
->name();
3972 static bool isTrait(const Class
* traitCls
) {
3973 return traitCls
->attrs() & AttrTrait
;
3976 static bool isAbstract(Attr modifiers
) {
3977 return modifiers
& AttrAbstract
;
3980 // Whether to exclude methods with name `methName' when adding.
3981 static bool exclude(const StringData
* methName
) {
3982 return Func::isSpecial(methName
);
3985 // TraitMethod constructor.
3986 static TraitMethod
traitMethod(const Class
* traitCls
,
3987 const Func
* traitMeth
,
3988 const PreClass::TraitAliasRule
& rule
) {
3989 return TraitMethod
{ traitCls
, traitMeth
, rule
.modifiers() };
3992 // Register a trait alias once the trait class is found.
3993 static void addTraitAlias(const Class
* cls
,
3994 const PreClass::TraitAliasRule
& rule
,
3995 const Class
* traitCls
) {
3996 PreClass::TraitAliasRule newRule
{ traitCls
->name(),
3997 rule
.origMethodName(),
3998 rule
.newMethodName(),
4000 cls
->addTraitAlias(newRule
);
4003 // Trait class/method finders.
4004 static const Class
* findSingleTraitWithMethod(const Class
* cls
,
4005 const StringData
* methName
) {
4006 Class
* traitCls
= nullptr;
4008 for (auto const& t
: cls
->usedTraitClasses()) {
4009 // Note: m_methods includes methods from parents/traits recursively.
4010 if (t
->lookupMethod(methName
)) {
4011 if (traitCls
!= nullptr) {
4012 raise_error("more than one trait contains method '%s'",
4021 static const Class
* findTraitClass(const Class
* cls
,
4022 const StringData
* traitName
) {
4023 auto ret
= Unit::loadClass(traitName
);
4024 if (!ret
) return nullptr;
4025 auto const& usedTraits
= cls
->preClass()->usedTraits();
4026 if (std::find_if(usedTraits
.begin(), usedTraits
.end(),
4027 [&] (auto const name
) {
4028 return traitName
->isame(name
);
4029 }) == usedTraits
.end()) {
4034 static const Func
* findTraitMethod(const Class
* traitCls
,
4035 const StringData
* origMethName
) {
4036 return traitCls
->lookupMethod(origMethName
);
4040 static void errorUnknownMethod(const StringData
* methName
) {
4041 raise_error(Strings::TRAITS_UNKNOWN_TRAIT_METHOD
, methName
->data());
4044 static void errorUnknownTrait(const StringData
* traitName
) {
4045 raise_error(Strings::TRAITS_UNKNOWN_TRAIT
, traitName
->data());
4047 static void errorDuplicateMethod(const Class
* cls
,
4048 const StringData
* methName
,
4049 const std::list
<TraitMethod
>& methods
) {
4050 // No error if the class will override the method.
4051 if (cls
->preClass()->hasMethod(methName
)) return;
4053 std::vector
<folly::StringPiece
> traitNames
;
4054 for (auto& method
: methods
) {
4055 traitNames
.push_back(method
.trait
->name()->slice());
4058 folly::join(", ", traitNames
, traits
);
4060 raise_error(Strings::METHOD_IN_MULTIPLE_TRAITS
, methName
->data(), traits
.c_str());
4062 static void errorInconsistentInsteadOf(const Class
* cls
,
4063 const StringData
* methName
) {
4064 raise_error(Strings::INCONSISTENT_INSTEADOF
, methName
->data(),
4065 cls
->name()->data(), cls
->name()->data());
4067 static void errorMultiplyExcluded(const StringData
* traitName
,
4068 const StringData
* methName
) {
4069 raise_error(Strings::MULTIPLY_EXCLUDED
,
4070 traitName
->data(), methName
->data());
4074 using TMIData
= TraitMethodImportData
<TraitMethod
, TMIOps
>;
4076 void applyTraitRules(Class
* cls
, TMIData
& tmid
) {
4077 for (auto const& precRule
: cls
->preClass()->traitPrecRules()) {
4078 tmid
.applyPrecRule(precRule
, cls
);
4080 for (auto const& aliasRule
: cls
->preClass()->traitAliasRules()) {
4081 tmid
.applyAliasRule(aliasRule
, cls
);
4085 void importTraitMethod(Class
* cls
,
4086 const TMIData::MethodData
& mdata
,
4087 Class::MethodMapBuilder
& builder
) {
4088 const Func
* method
= mdata
.tm
.method
;
4089 Attr modifiers
= mdata
.tm
.modifiers
;
4091 auto mm_iter
= builder
.find(mdata
.name
);
4093 ITRACE(5, " - processing trait method {}\n", method
->name()->data());
4095 // For abstract methods, simply return if method already declared.
4096 if ((modifiers
& AttrAbstract
) && mm_iter
!= builder
.end()) {
4100 if (modifiers
== AttrNone
) {
4101 modifiers
= method
->attrs();
4103 // Trait alias statements are only allowed to change the attributes that
4104 // are part 'attrMask' below; all other method attributes are preserved
4105 Attr attrMask
= (Attr
)(AttrPublic
| AttrProtected
| AttrPrivate
|
4106 AttrAbstract
| AttrFinal
);
4107 modifiers
= (Attr
)((modifiers
& (attrMask
)) |
4108 (method
->attrs() & ~(attrMask
)));
4111 Func
* parentMethod
= nullptr;
4112 if (mm_iter
!= builder
.end()) {
4113 Func
* existingMethod
= builder
[mm_iter
->second
];
4114 if (existingMethod
->cls() == cls
) {
4115 // Don't override an existing method if this class provided an
4119 parentMethod
= existingMethod
;
4121 Func
* f
= method
->clone(cls
, mdata
.name
);
4123 f
->setAttrs(modifiers
| AttrTrait
);
4124 if (!parentMethod
) {
4126 builder
.add(mdata
.name
, f
);
4128 f
->setHasPrivateAncestor(false);
4130 // Override an existing method
4133 cls
->methodOverrideCheck(parentMethod
, f
);
4135 assertx(!(f
->attrs() & AttrPrivate
) ||
4136 (parentMethod
->attrs() & AttrPrivate
));
4137 if ((parentMethod
->attrs() & AttrPrivate
) || (f
->attrs() & AttrPrivate
)) {
4140 baseClass
= parentMethod
->baseCls();
4142 f
->setBaseCls(baseClass
);
4143 f
->setHasPrivateAncestor(
4144 parentMethod
->hasPrivateAncestor() ||
4145 (parentMethod
->attrs() & AttrPrivate
));
4146 builder
[mm_iter
->second
] = f
;
4152 void Class::importTraitMethods(MethodMapBuilder
& builder
) {
4155 // Find all methods to be imported.
4156 for (auto const& t
: m_extra
->m_usedTraits
) {
4157 Class
* trait
= t
.get();
4158 for (Slot i
= 0; i
< trait
->m_methods
.size(); ++i
) {
4159 auto const method
= trait
->getMethod(i
);
4161 TraitMethod traitMethod
{ trait
, method
, method
->attrs() };
4162 tmid
.add(traitMethod
, method
->name());
4166 // Apply trait rules and import the methods.
4167 applyTraitRules(this, tmid
);
4168 auto traitMethods
= tmid
.finish(this);
4170 // Import the methods.
4171 for (auto const& mdata
: traitMethods
) {
4172 importTraitMethod(this, mdata
, builder
);
4176 ///////////////////////////////////////////////////////////////////////////////