2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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/type-structure.h"
25 #include "hphp/runtime/vm/jit/translator.h"
26 #include "hphp/runtime/vm/instance-bits.h"
27 #include "hphp/runtime/vm/native-data.h"
28 #include "hphp/runtime/vm/native-prop-handler.h"
29 #include "hphp/runtime/vm/treadmill.h"
31 #include "hphp/runtime/ext/string/ext_string.h"
33 #include "hphp/system/systemlib.h"
34 #include "hphp/parser/parser.h"
36 #include "hphp/util/debug.h"
37 #include "hphp/util/logger.h"
39 #include <folly/Bits.h>
44 TRACE_SET_MOD(class_load
);
47 ///////////////////////////////////////////////////////////////////////////////
49 const StaticString
s_86ctor("86ctor");
50 const StaticString
s_86pinit("86pinit");
51 const StaticString
s_86sinit("86sinit");
53 hphp_hash_map
<const StringData
*, const HhbcExtClassInfo
*,
54 string_data_hash
, string_data_isame
> Class::s_extClassHash
;
56 void (*Class::MethodCreateHook
)(Class
* cls
, MethodMapBuilder
& builder
);
60 ///////////////////////////////////////////////////////////////////////////////
63 * We clone methods with static locals into derived classes, but the clone
64 * still points to the class the method was defined in (because it needs to
65 * have the right context class). For data profiling, we need to find the
66 * actual class that a Func belongs to so we put such Funcs into this map.
68 typedef tbb::concurrent_hash_map
<uint64_t, const Class
*> FuncIdToClassMap
;
69 static FuncIdToClassMap
* s_funcIdToClassMap
;
71 const Class
* getOwningClassForFunc(const Func
* f
) {
72 // We only populate s_funcIdToClassMap when EvalPerfDataMap is true.
73 assert(RuntimeOption::EvalPerfDataMap
);
75 if (s_funcIdToClassMap
) {
76 FuncIdToClassMap::const_accessor acc
;
77 if (s_funcIdToClassMap
->find(acc
, f
->getFuncId())) {
85 ///////////////////////////////////////////////////////////////////////////////
86 // Class::PropInitVec.
88 Class::PropInitVec::PropInitVec()
91 , m_req_allocated(false)
94 Class::PropInitVec::~PropInitVec() {
95 if (!m_req_allocated
) free(m_data
);
99 Class::PropInitVec::allocWithReqAllocator(const PropInitVec
& src
) {
100 PropInitVec
* p
= req::make_raw
<PropInitVec
>();
101 p
->m_size
= src
.size();
102 p
->m_data
= req::make_raw_array
<TypedValueAux
>(src
.size());
103 memcpy(p
->m_data
, src
.m_data
, src
.size() * sizeof(*p
->m_data
));
104 p
->m_req_allocated
= true;
108 const Class::PropInitVec
&
109 Class::PropInitVec::operator=(const PropInitVec
& piv
) {
110 assert(!m_req_allocated
);
112 unsigned sz
= m_size
= piv
.size();
113 if (sz
) sz
= folly::nextPowTwo(sz
);
115 m_data
= (TypedValueAux
*)malloc(sz
* sizeof(*m_data
));
117 memcpy(m_data
, piv
.m_data
, piv
.size() * sizeof(*m_data
));
122 void Class::PropInitVec::push_back(const TypedValue
& v
) {
123 assert(!m_req_allocated
);
125 * the allocated size is always the next power of two (or zero)
126 * so we just need to reallocate when we hit a power of two
128 if (!m_size
|| folly::isPowTwo(m_size
)) {
129 unsigned size
= m_size
? m_size
* 2 : 1;
130 m_data
= (TypedValueAux
*)realloc(m_data
, size
* sizeof(*m_data
));
133 cellDup(v
, m_data
[m_size
++]);
137 ///////////////////////////////////////////////////////////////////////////////
143 * Load used traits of PreClass `preClass', and append the trait Class*'s to
144 * 'usedTraits'. Return an estimate of the method count of all used traits.
146 unsigned loadUsedTraits(PreClass
* preClass
,
147 std::vector
<ClassPtr
>& usedTraits
) {
148 unsigned methodCount
= 0;
149 for (auto const& traitName
: preClass
->usedTraits()) {
150 Class
* classPtr
= Unit::loadClass(traitName
);
151 if (classPtr
== nullptr) {
152 raise_error(Strings::TRAITS_UNKNOWN_TRAIT
, traitName
->data());
154 if (!(classPtr
->attrs() & AttrTrait
)) {
155 raise_error("%s cannot use %s - it is not a trait",
156 preClass
->name()->data(),
157 classPtr
->name()->data());
160 if (RuntimeOption::RepoAuthoritative
) {
161 // In RepoAuthoritative mode (with the WholeProgram compiler
162 // optimizations), the contents of traits are flattened away into the
163 // preClasses of "use"r classes. Continuing here allows us to avoid
164 // unnecessarily attempting to re-import trait methods and
165 // properties, only to fail due to (surprise surprise!) the same
166 // method/property existing on m_preClass.
170 usedTraits
.push_back(ClassPtr(classPtr
));
171 methodCount
+= classPtr
->numMethods();
175 if (!RuntimeOption::RepoAuthoritative
) {
176 // Trait aliases can increase method count. Get an estimate of the
177 // number of aliased functions. This doesn't need to be done in
178 // RepoAuthoritative mode due to trait flattening ensuring that added
179 // methods are already present in the preclass.
180 for (auto const& rule
: preClass
->traitAliasRules()) {
181 auto origName
= rule
.origMethodName();
182 auto newName
= rule
.newMethodName();
183 if (origName
!= newName
) methodCount
++;
190 * Class ends with a dynamically sized array, m_classVec. C++ doesn't allow
191 * declaring empty arrays like C does, so we give it size 1 and use
192 * m_classVec's offset as the true size of Class when allocating memory to
195 constexpr size_t sizeof_Class
= Class::classVecOff();
198 struct assert_sizeof_class
{
199 // If this static_assert fails, the compiler error will have the real value
200 // of sizeof_Class in it since it's in this struct's type.
201 static_assert(sz
== (use_lowptr
? 252 : 296), "Change this only on purpose");
203 template struct assert_sizeof_class
<sizeof_Class
>;
206 * R/W lock for caching scopings of closures.
208 ReadWriteMutex s_scope_cache_mutex
;
212 Class
* Class::newClass(PreClass
* preClass
, Class
* parent
) {
213 auto const classVecLen
= parent
!= nullptr ? parent
->m_classVecLen
+ 1 : 1;
214 auto funcVecLen
= (parent
!= nullptr ? parent
->m_methods
.size() : 0)
215 + preClass
->numMethods();
217 std::vector
<ClassPtr
> usedTraits
;
218 auto numTraitMethodsEstimate
= loadUsedTraits(preClass
, usedTraits
);
219 // In RepoAuthoritative mode, trait methods are already flattened
220 // into the preClass, so we don't need to add in the estimate here.
221 if (!RuntimeOption::RepoAuthoritative
) {
222 funcVecLen
+= numTraitMethodsEstimate
;
225 auto const size
= sizeof_Class
226 + sizeof(m_classVec
[0]) * classVecLen
227 + sizeof(LowPtr
<Func
>) * funcVecLen
;
228 auto const mem
= low_malloc(size
);
229 auto const classPtr
= (void *)((uintptr_t)mem
+
230 funcVecLen
* sizeof(LowPtr
<Func
>));
232 return new (classPtr
) Class(preClass
, parent
, std::move(usedTraits
),
233 classVecLen
, funcVecLen
);
240 Class
* Class::rescope(Class
* ctx
, Attr attrs
/* = AttrNone */) {
241 assert(parent() == SystemLib::s_ClosureClass
);
244 bool const is_dynamic
= (attrs
!= AttrNone
);
246 // Look up the generated template class for this particular subclass of
247 // Closure. This class maintains the table of scoped clones of itself, and
248 // if we create a new scoped clone, we need to map it there.
249 auto template_cls
= is_dynamic
? Unit::lookupClass(name()) : this;
250 auto const invoke
= template_cls
->m_invoke
;
252 assert(IMPLIES(is_dynamic
, m_scoped
));
253 assert(IMPLIES(is_dynamic
, template_cls
->m_scoped
));
255 auto const try_template
= [&]() -> Class
* {
256 bool const ctx_match
= invoke
->cls() == ctx
;
257 bool const attrs_match
= (attrs
== AttrNone
|| attrs
== invoke
->attrs());
259 return ctx_match
&& attrs_match
? template_cls
: nullptr;
262 // If the template class has already been scoped to `ctx', we're done. This
263 // is the common case in repo mode.
264 if (auto cls
= try_template()) return cls
;
266 template_cls
->allocExtraData();
267 auto& scopedClones
= template_cls
->m_extra
.raw()->m_scopedClones
;
269 auto const key
= reinterpret_cast<uintptr_t>(ctx
) | uintptr_t(attrs
) << 32;
271 auto const try_cache
= [&] {
272 auto it
= scopedClones
.find(key
);
273 return it
!= scopedClones
.end() ? it
->second
.get() : nullptr;
276 { // Return the cached clone if we have one.
277 ReadLock
l(s_scope_cache_mutex
);
279 // This assertion only holds under lock, since setting m_scoped and
280 // m_invoke->cls() are independent atomic operations.
281 assert(template_cls
->m_scoped
== (invoke
->cls() != template_cls
));
283 // If this succeeds, someone raced us to scoping the template. We may have
284 // unnecessarily allocated an ExtraData, but whatever.
285 if (auto cls
= try_template()) return cls
;
287 if (auto cls
= try_cache()) return cls
;
290 // We use the French for closure because using the English crashes gcc in the
291 // implicit lambda capture below. (This is fixed in gcc 4.8.5.)
292 auto fermeture
= ClassPtr
{
293 template_cls
->m_scoped
294 ? newClass(m_preClass
.get(), m_parent
.get())
298 WriteLock
l(s_scope_cache_mutex
);
300 // Check the caches again.
301 if (auto cls
= try_template()) return cls
;
302 if (auto cls
= try_cache()) return cls
;
304 fermeture
->m_invoke
->rescope(ctx
, attrs
);
305 fermeture
->m_scoped
= true;
307 InstanceBits::ifInitElse(
308 [&] { fermeture
->setInstanceBits();
309 if (this != fermeture
.get()) scopedClones
[key
] = fermeture
; },
310 [&] { if (this != fermeture
.get()) scopedClones
[key
] = fermeture
; }
313 return fermeture
.get();
316 void Class::destroy() {
318 * If we were never put on NamedEntity::classList, or
319 * we've already been destroy'd, there's nothing to do
321 if (!m_cachedClass
.bound()) return;
323 Lock
l(g_classesMutex
);
324 // Need to recheck now we have the lock
325 if (!m_cachedClass
.bound()) return;
326 // Only do this once.
327 m_cachedClass
= rds::Link
<Class
*>(rds::kInvalidHandle
);
330 * Regardless of refCount, this Class is now unusable. Remove it
331 * from the class list.
333 * Needs to be under the lock, because multiple threads could call
334 * destroy, or want to manipulate the class list. (It's safe for
335 * other threads to concurrently read the class list without the
338 auto const pcls
= m_preClass
.get();
339 pcls
->namedEntity()->removeClass(this);
343 if (!this->decAtomicCount()) this->atomicRelease();
348 void Class::atomicRelease() {
349 assert(!m_cachedClass
.bound());
352 low_free(mallocPtr());
356 releaseRefs(); // must be called for Func-nulling side effects
359 for (unsigned i
= 0, n
= numStaticProperties(); i
< n
; ++i
) {
360 m_sPropCache
[i
].~Link();
365 for (auto i
= size_t{}, n
= numMethods(); i
< n
; i
++) {
366 if (auto meth
= getMethod(i
)) {
367 if (meth
->isPreFunc()) {
376 delete m_extra
.raw();
380 EnumCache::deleteValues(this);
382 low_free(m_vtableVec
.get());
385 void Class::releaseRefs() {
387 * We have to be careful here.
388 * We want to free up as much as possible as early as possible, but
389 * some of our methods may actually belong to our parent
390 * This means we can't destroy *our* Funcs until our refCount
391 * hits zero (ie when Class::~Class gets called), because there
392 * could be a child class which hasn't yet been destroyed, which
393 * will need to inspect them. Also, we need to inspect the Funcs
394 * now (while we still have a references to parent) to determine
395 * which ones we will eventually need to free.
396 * Similarly, if any of our func's belong to a parent class, we
397 * can't free the parent, because one of our children could also
398 * have a reference to those func's (and its only reference to
399 * our parent is via this class).
401 auto num
= numMethods();
402 bool okToReleaseParent
= true;
403 for (auto i
= 0; i
< num
; i
++) {
404 Func
* meth
= getMethod(i
);
405 if (meth
/* releaseRefs can be called more than once */ &&
406 meth
->cls() != this &&
407 ((meth
->attrs() & AttrPrivate
) || !meth
->hasStaticLocals())) {
408 setMethod(i
, nullptr);
409 okToReleaseParent
= false;
413 if (okToReleaseParent
) {
417 m_numDeclInterfaces
= 0;
418 m_declInterfaces
.reset();
419 m_requirements
.clear();
422 auto xtra
= m_extra
.raw();
423 xtra
->m_usedTraits
.clear();
427 Class::Avail
Class::avail(Class
*& parent
,
428 bool tryAutoload
/* = false */) const {
429 if (Class
*ourParent
= m_parent
.get()) {
431 PreClass
*ppcls
= ourParent
->m_preClass
.get();
432 parent
= Unit::getClass(ppcls
->namedEntity(),
433 m_preClass
.get()->parent(), tryAutoload
);
439 if (parent
!= ourParent
) {
440 if (UNLIKELY(ourParent
->isZombie())) {
441 const_cast<Class
*>(this)->destroy();
447 for (size_t i
= 0; i
< m_numDeclInterfaces
; i
++) {
448 auto di
= m_declInterfaces
.get()[i
].get();
449 const StringData
* pdi
= m_preClass
.get()->interfaces()[i
];
450 assert(pdi
->isame(di
->name()));
452 PreClass
*pint
= di
->m_preClass
.get();
453 Class
* interface
= Unit::getClass(pint
->namedEntity(), pdi
,
455 if (interface
!= di
) {
456 if (interface
== nullptr) {
460 if (UNLIKELY(di
->isZombie())) {
461 const_cast<Class
*>(this)->destroy();
467 for (size_t i
= 0, n
= m_extra
->m_usedTraits
.size(); i
< n
; ++i
) {
468 auto usedTrait
= m_extra
->m_usedTraits
.at(i
).get();
469 const StringData
* usedTraitName
= m_preClass
.get()->usedTraits()[i
];
470 PreClass
* ptrait
= usedTrait
->m_preClass
.get();
471 Class
* trait
= Unit::getClass(ptrait
->namedEntity(), usedTraitName
,
473 if (trait
!= usedTrait
) {
474 if (trait
== nullptr) {
478 if (UNLIKELY(usedTrait
->isZombie())) {
479 const_cast<Class
*>(this)->destroy();
488 ///////////////////////////////////////////////////////////////////////////////
489 // Pre- and post-allocations.
491 LowPtr
<Func
>* Class::funcVec() const {
492 return reinterpret_cast<LowPtr
<Func
>*>(
493 reinterpret_cast<uintptr_t>(this) -
494 m_funcVecLen
* sizeof(LowPtr
<Func
>)
498 void* Class::mallocPtr() const {
503 ///////////////////////////////////////////////////////////////////////////////
506 const Class
* Class::commonAncestor(const Class
* cls
) const {
507 assert(isNormalClass(this) && isNormalClass(cls
));
509 // Walk up m_classVec for both classes to look for a common ancestor.
510 auto vecIdx
= std::min(m_classVecLen
, cls
->m_classVecLen
) - 1;
512 assert(vecIdx
< m_classVecLen
&& vecIdx
< cls
->m_classVecLen
);
513 if (m_classVec
[vecIdx
] == cls
->m_classVec
[vecIdx
]) {
514 return m_classVec
[vecIdx
];
522 ///////////////////////////////////////////////////////////////////////////////
525 const Func
* Class::getDeclaredCtor() const {
526 const Func
* f
= getCtor();
527 return f
->name() != s_86ctor
.get() ? f
: nullptr;
530 const Func
* Class::getCachedInvoke() const {
531 assert(IMPLIES(m_invoke
, !m_invoke
->isStatic() || m_invoke
->isClosureBody()));
535 ///////////////////////////////////////////////////////////////////////////////
538 bool Class::isCppSerializable() const {
539 assert(instanceCtor()); // Only call this on CPP classes
540 auto* ndi
= m_extra
? m_extra
.raw()->m_nativeDataInfo
: nullptr;
541 if (ndi
!= nullptr && ndi
->isSerializable()) {
544 auto info
= clsInfo();
546 while ((!info
) && (p
= p
->parent())) {
550 (info
->getAttribute() & ClassInfo::IsCppSerializable
);
553 bool Class::isCollectionClass() const {
555 return collections::isTypeName(s
);
559 ///////////////////////////////////////////////////////////////////////////////
560 // Property initialization.
562 void Class::initialize() const {
563 if (m_pinitVec
.size() > 0 && getPropData() == nullptr) {
566 if (numStaticProperties() > 0 && needsInitSProps()) {
571 bool Class::initialized() const {
572 if (m_pinitVec
.size() > 0 && getPropData() == nullptr) {
575 if (numStaticProperties() > 0 && needsInitSProps()) {
581 void Class::initProps() const {
582 assert(m_pinitVec
.size() > 0);
583 assert(getPropData() == nullptr);
584 // Copy initial values for properties to a new vector that can be used to
585 // complete initialization for non-scalar properties via the iterative
586 // 86pinit() calls below. 86pinit() takes a reference to an array to populate
587 // with initial property values; after it completes, we copy the values into
589 auto propVec
= PropInitVec::allocWithReqAllocator(m_declPropInit
);
592 *m_propDataCache
= propVec
;
595 // Iteratively invoke 86pinit() methods upward
596 // through the inheritance chain.
597 for (auto it
= m_pinitVec
.rbegin(); it
!= m_pinitVec
.rend(); ++it
) {
599 g_context
->invokeFunc(&retval
, *it
, init_null_variant
, nullptr,
600 const_cast<Class
*>(this));
601 assert(retval
.m_type
== KindOfNull
);
604 // Undo the allocation of propVec
605 req::destroy_raw_array(propVec
->begin(), propVec
->size());
606 req::destroy_raw(propVec
);
607 *m_propDataCache
= nullptr;
611 // For properties that do not require deep initialization, promote strings
612 // and arrays that came from 86pinit to static. This allows us to initialize
613 // object properties very quickly because we can just memcpy and we don't
614 // have to do any refcounting.
615 // For properties that require "deep" initialization, we have to do a little
616 // more work at object creation time.
618 for (auto it
= propVec
->begin(); it
!= propVec
->end(); ++it
, ++slot
) {
619 TypedValueAux
* tv
= &(*it
);
620 // Set deepInit if the property requires "deep" initialization.
621 if (m_declProperties
[slot
].attrs
& AttrDeepInit
) {
622 tv
->deepInit() = true;
624 tvAsVariant(tv
).setEvalScalar();
625 tv
->deepInit() = false;
630 bool Class::needsInitSProps() const {
631 return !m_sPropCacheInit
.bound() || !*m_sPropCacheInit
;
634 void Class::initSProps() const {
635 assert(needsInitSProps() || m_sPropCacheInit
.isPersistent());
637 // Initialize static props for parent.
638 Class
* parent
= this->parent();
639 if (parent
&& parent
->needsInitSProps()) {
640 parent
->initSProps();
643 if (!numStaticProperties()) return;
647 // Perform scalar inits.
648 for (Slot slot
= 0, n
= m_staticProperties
.size(); slot
< n
; ++slot
) {
649 auto const& sProp
= m_staticProperties
[slot
];
651 if (sProp
.cls
== this && !m_sPropCache
[slot
].isPersistent()) {
652 *m_sPropCache
[slot
] = sProp
.val
;
656 const bool hasNonscalarInit
= !m_sinitVec
.empty();
658 // If there are non-scalar initializers (i.e. 86sinit methods), run them now.
659 // They will override the KindOfUninit values set by scalar initialization.
660 if (hasNonscalarInit
) {
661 for (unsigned i
= 0, n
= m_sinitVec
.size(); i
< n
; i
++) {
663 g_context
->invokeFunc(&retval
, m_sinitVec
[i
], init_null_variant
,
664 nullptr, const_cast<Class
*>(this));
665 assert(retval
.m_type
== KindOfNull
);
669 *m_sPropCacheInit
= true;
673 ///////////////////////////////////////////////////////////////////////////////
676 void Class::initSPropHandles() const {
677 if (m_sPropCacheInit
.bound()) return;
679 bool usePersistentHandles
= m_cachedClass
.isPersistent();
680 bool allPersistentHandles
= usePersistentHandles
;
682 // Propagate to parents so we can link inherited static props.
683 Class
* parent
= this->parent();
685 parent
->initSPropHandles();
686 if (!rds::isPersistentHandle(parent
->sPropInitHandle())) {
687 allPersistentHandles
= false;
691 // Bind all the static prop handles.
692 for (Slot slot
= 0, n
= m_staticProperties
.size(); slot
< n
; ++slot
) {
693 auto& propHandle
= m_sPropCache
[slot
];
694 auto const& sProp
= m_staticProperties
[slot
];
696 if (!propHandle
.bound()) {
697 if (sProp
.cls
== this) {
698 if (usePersistentHandles
&& (sProp
.attrs
& AttrPersistent
)) {
699 propHandle
.bind(rds::Mode::Persistent
);
700 *propHandle
= sProp
.val
;
702 propHandle
.bind(rds::Mode::Local
);
705 auto msg
= name()->toCppString() + "::" + sProp
.name
->toCppString();
706 rds::recordRds(propHandle
.handle(),
707 sizeof(TypedValue
), "SPropCache", msg
);
709 auto realSlot
= sProp
.cls
->lookupSProp(sProp
.name
);
710 propHandle
= sProp
.cls
->m_sPropCache
[realSlot
];
712 } else if (propHandle
.isPersistent() && sProp
.cls
== this) {
714 * Avoid a weird race: two threads come through at once, the first
715 * gets as far as binding propHandle, but then sleeps. Meanwhile the
716 * second sees that its been bound, finishes up, and then tries to
717 * read the property, but sees uninit-null for the value (and asserts
720 *propHandle
= sProp
.val
;
722 if (!propHandle
.isPersistent()) {
723 allPersistentHandles
= false;
727 // Bind the init handle; this indicates that all handles are bound.
728 if (allPersistentHandles
) {
729 // We must make sure the value stored at the handle is correct before
730 // setting m_sPropCacheInit in case another thread tries to read it at just
732 rds::Link
<bool> tmp
{rds::kInvalidHandle
};
733 tmp
.bind(rds::Mode::Persistent
);
735 m_sPropCacheInit
= tmp
;
737 m_sPropCacheInit
.bind();
739 rds::recordRds(m_sPropCacheInit
.handle(),
740 sizeof(bool), "SPropCacheInit", name()->data());
743 Class::PropInitVec
* Class::getPropData() const {
744 return m_propDataCache
.bound() ? *m_propDataCache
: nullptr;
747 TypedValue
* Class::getSPropData(Slot index
) const {
748 assert(numStaticProperties() > index
);
749 return m_sPropCache
[index
].bound() ? m_sPropCache
[index
].get() : nullptr;
753 ///////////////////////////////////////////////////////////////////////////////
754 // Property lookup and accessibility.
756 Class::PropLookup
<Slot
> Class::getDeclPropIndex(
758 const StringData
* key
760 auto const propInd
= lookupDeclProp(key
);
762 auto accessible
= false;
764 if (propInd
!= kInvalidSlot
) {
765 auto const attrs
= m_declProperties
[propInd
].attrs
;
766 if ((attrs
& (AttrProtected
|AttrPrivate
)) &&
767 !g_context
->debuggerSettings
.bypassCheck
) {
768 // Fetch the class in the inheritance tree which first declared the
770 auto const baseClass
= m_declProperties
[propInd
].cls
;
773 // If ctx == baseClass, we have the right property and we can stop here.
774 if (ctx
== baseClass
) return PropLookup
<Slot
> { propInd
, true };
776 // The anonymous context cannot access protected or private properties, so
777 // we can fail fast here.
778 if (ctx
== nullptr) return PropLookup
<Slot
> { propInd
, false };
781 if (attrs
& AttrPrivate
) {
782 // ctx != baseClass and the property is private, so it is not
783 // accessible. We need to keep going because ctx may define a private
784 // property with this name.
787 if (ctx
== (Class
*)-1 || ctx
->classof(baseClass
)) {
788 // The special ctx (Class*)-1 is used by unserialization to
789 // mean that protected properties are ok. Otherwise,
790 // ctx is derived from baseClass, so we know this protected
791 // property is accessible and we know ctx cannot have private
792 // property with the same name, so we're done.
793 return PropLookup
<Slot
> { propInd
, true };
795 if (!baseClass
->classof(ctx
)) {
796 // ctx is not the same, an ancestor, or a descendent of baseClass,
797 // so the property is not accessible. Also, we know that ctx cannot
798 // be the same or an ancestor of this, so we don't need to check if
799 // ctx declares a private property with the same name and we can
801 return PropLookup
<Slot
> { propInd
, false };
803 // We now know this protected property is accessible, but we need to
804 // keep going because ctx may define a private property with the same
807 assert(baseClass
->classof(ctx
));
810 // The property is public (or we're in the debugger and we are bypassing
811 // accessibility checks).
813 // If ctx == this, we don't have to check if ctx defines a private
814 // property with the same name and we can stop here.
815 if (ctx
== this) return PropLookup
<Slot
> { propInd
, true };
817 // We still need to check if ctx defines a private property with the same
821 // We didn't find a visible declared property in this's property map
825 // If ctx is an ancestor of this, check if ctx has a private property with the
827 if (ctx
&& ctx
!= (Class
*)-1 && classof(ctx
)) {
828 auto const ctxPropInd
= ctx
->lookupDeclProp(key
);
830 if (ctxPropInd
!= kInvalidSlot
&&
831 ctx
->m_declProperties
[ctxPropInd
].cls
== ctx
&&
832 (ctx
->m_declProperties
[ctxPropInd
].attrs
& AttrPrivate
)) {
833 // A private property from ctx trumps any other property we may
835 return PropLookup
<Slot
> { ctxPropInd
, true };
839 return PropLookup
<Slot
> { propInd
, accessible
};
842 Class::PropLookup
<Slot
> Class::findSProp(
844 const StringData
* sPropName
846 auto const sPropInd
= lookupSProp(sPropName
);
848 // Non-existent property.
849 if (sPropInd
== kInvalidSlot
) return PropLookup
<Slot
> { kInvalidSlot
, false };
851 // Property access within this Class's context.
852 if (ctx
== this) return PropLookup
<Slot
> { sPropInd
, true };
854 auto const sPropAttrs
= m_staticProperties
[sPropInd
].attrs
;
856 auto const accessible
= [&] {
857 switch (sPropAttrs
& (AttrPublic
| AttrProtected
| AttrPrivate
)) {
858 // Public properties are always accessible.
862 // Property access is from within a parent class's method, which is
863 // allowed for protected properties.
865 return ctx
!= nullptr && (classof(ctx
) || ctx
->classof(this));
867 // Can only access private properties via the debugger.
869 return g_context
->debuggerSettings
.bypassCheck
;
876 return PropLookup
<Slot
> { sPropInd
, accessible
};
879 Class::PropLookup
<TypedValue
*> Class::getSProp(
881 const StringData
* sPropName
885 auto const lookup
= findSProp(ctx
, sPropName
);
886 if (lookup
.prop
== kInvalidSlot
) {
887 return PropLookup
<TypedValue
*> { nullptr, false };
890 auto const sProp
= getSPropData(lookup
.prop
);
891 assert(sProp
&& sProp
->m_type
!= KindOfUninit
&&
892 "Static property initialization failed to initialize a property.");
893 return PropLookup
<TypedValue
*> { sProp
, lookup
.accessible
};
896 bool Class::IsPropAccessible(const Prop
& prop
, Class
* ctx
) {
897 if (prop
.attrs
& AttrPublic
) return true;
898 if (prop
.attrs
& AttrPrivate
) return prop
.cls
== ctx
;
899 if (!ctx
) return false;
901 return prop
.cls
->classof(ctx
) || ctx
->classof(prop
.cls
);
905 ///////////////////////////////////////////////////////////////////////////////
908 Cell
Class::clsCnsGet(const StringData
* clsCnsName
, bool includeTypeCns
) const {
910 auto clsCns
= cnsNameToTV(clsCnsName
, clsCnsInd
, includeTypeCns
);
911 if (!clsCns
) return make_tv
<KindOfUninit
>();
912 if (clsCns
->m_type
!= KindOfUninit
&& !m_constants
[clsCnsInd
].isType()) {
916 // This constant has a non-scalar initializer, meaning it will be
917 // potentially different in different requests, which we store
918 // separately in an array living off in RDS.
919 m_nonScalarConstantCache
.bind();
920 auto& clsCnsData
= *m_nonScalarConstantCache
;
922 if (clsCnsData
.get() == nullptr) {
923 clsCnsData
= Array::attach(MixedArray::MakeReserve(m_constants
.size()));
925 auto cCns
= clsCnsData
->nvGet(clsCnsName
);
926 if (cCns
) return *cCns
;
929 // resolve type constant
930 if (m_constants
[clsCnsInd
].isType()) {
933 resTS
= TypeStructure::resolve(m_constants
[clsCnsInd
], this);
934 } catch (Exception
&e
) {
935 raise_error(e
.getMessage());
937 auto tv
= make_tv
<KindOfArray
>(resTS
.get());
938 tv
.m_aux
= clsCns
->m_aux
;
939 assert(tvIsPlausible(tv
));
940 clsCnsData
.set(StrNR(clsCnsName
), tvAsCVarRef(&tv
), true /* isKey */);
944 // The class constant has not been initialized yet; do so.
945 static auto const sd86cinit
= makeStaticString("86cinit");
946 auto const meth86cinit
=
947 m_constants
[clsCnsInd
].cls
->lookupMethod(sd86cinit
);
948 TypedValue args
[1] = {
949 make_tv
<KindOfStaticString
>(
950 const_cast<StringData
*>(m_constants
[clsCnsInd
].name
.get()))
954 g_context
->invokeFuncFew(
957 ActRec::encodeClass(this),
963 clsCnsData
.set(StrNR(clsCnsName
), cellAsCVarRef(ret
), true /* isKey */);
967 const Cell
* Class::cnsNameToTV(const StringData
* clsCnsName
,
969 bool includeTypeCns
) const {
970 clsCnsInd
= m_constants
.findIndex(clsCnsName
);
971 if (clsCnsInd
== kInvalidSlot
) {
974 if (m_constants
[clsCnsInd
].isAbstract()) {
977 if (!includeTypeCns
&& m_constants
[clsCnsInd
].isType()) {
980 auto const ret
= const_cast<TypedValueAux
*>(&m_constants
[clsCnsInd
].val
);
981 assert(tvIsPlausible(*ret
));
985 DataType
Class::clsCnsType(const StringData
* cnsName
) const {
987 auto const cns
= cnsNameToTV(cnsName
, slot
);
988 // TODO(#2913342): lookup the constant in RDS in case it's dynamic
989 // and already initialized.
990 if (!cns
) return KindOfUninit
;
995 ///////////////////////////////////////////////////////////////////////////////
998 size_t Class::declPropOffset(Slot index
) const {
999 static_assert(std::is_unsigned
<Slot
>::value
,
1000 "Slot is supposed to be unsigned");
1001 return sizeof(ObjectData
) +
1002 m_extra
->m_builtinODTailSize
+
1003 index
* sizeof(TypedValue
);
1007 ///////////////////////////////////////////////////////////////////////////////
1010 bool Class::verifyPersistent() const {
1011 if (!(attrs() & AttrPersistent
)) return false;
1012 if (m_parent
.get() &&
1013 !rds::isPersistentHandle(m_parent
->classHandle())) {
1016 for (auto const& declInterface
: declInterfaces()) {
1017 if (!rds::isPersistentHandle(declInterface
->classHandle())) {
1021 for (auto const& usedTrait
: m_extra
->m_usedTraits
) {
1022 if (!rds::isPersistentHandle(usedTrait
->classHandle())) {
1029 void Class::setInstanceBits() {
1030 setInstanceBitsImpl
<false>();
1032 void Class::setInstanceBitsAndParents() {
1033 setInstanceBitsImpl
<true>();
1036 template<bool setParents
>
1037 void Class::setInstanceBitsImpl() {
1038 // Bit 0 is reserved to indicate whether or not the rest of the bits
1039 // are initialized yet.
1040 if (m_instanceBits
.test(0)) return;
1042 InstanceBits::BitSet bits
;
1044 auto setBits
= [&](Class
* c
) {
1045 if (setParents
) c
->setInstanceBitsAndParents();
1046 bits
|= c
->m_instanceBits
;
1048 if (m_parent
.get()) setBits(m_parent
.get());
1050 int numIfaces
= m_interfaces
.size();
1051 for (int i
= 0; i
< numIfaces
; i
++) setBits(m_interfaces
[i
]);
1053 // XXX: this assert fails on the initFlag; oops.
1054 if (unsigned bit
= InstanceBits::lookup(m_preClass
->name())) {
1057 m_instanceBits
= bits
;
1061 ///////////////////////////////////////////////////////////////////////////////
1064 // These are mostly for the class creation path.
1066 void Class::setParent() {
1067 // Cache m_preClass->attrs()
1068 m_attrCopy
= m_preClass
->attrs();
1070 // Validate the parent
1071 if (m_parent
.get() != nullptr) {
1072 Attr parentAttrs
= m_parent
->attrs();
1073 if (UNLIKELY(parentAttrs
&
1074 (AttrFinal
| AttrInterface
| AttrTrait
| AttrEnum
))) {
1075 static StringData
* sd___MockClass
= makeStaticString("__MockClass");
1076 if (!(parentAttrs
& AttrFinal
) ||
1077 (parentAttrs
& AttrEnum
) ||
1078 m_preClass
->userAttributes().find(sd___MockClass
) ==
1079 m_preClass
->userAttributes().end() ||
1080 m_parent
->isCollectionClass()) {
1081 raise_error("Class %s may not inherit from %s (%s)",
1082 m_preClass
->name()->data(),
1083 ((parentAttrs
& AttrEnum
) ? "enum" :
1084 (parentAttrs
& AttrFinal
) ? "final class" :
1085 (parentAttrs
& AttrInterface
) ? "interface" : "trait"),
1086 m_parent
->name()->data());
1088 if ((parentAttrs
& AttrAbstract
) &&
1089 ((m_attrCopy
& (AttrAbstract
|AttrFinal
)) != (AttrAbstract
|AttrFinal
))) {
1091 "Class %s with %s inheriting 'abstract final' class %s"
1092 " must also be 'abstract final'",
1093 m_preClass
->name()->data(),
1094 sd___MockClass
->data(),
1095 m_parent
->name()->data()
1101 // Handle stuff specific to cppext classes
1102 if (m_preClass
->instanceCtor()) {
1104 m_extra
.raw()->m_instanceCtor
= m_preClass
->instanceCtor();
1105 m_extra
.raw()->m_instanceDtor
= m_preClass
->instanceDtor();
1106 m_extra
.raw()->m_builtinODTailSize
= m_preClass
->builtinObjSize() -
1107 m_preClass
->builtinODOffset();
1108 m_extra
.raw()->m_clsInfo
=
1109 ClassInfo::FindSystemClassInterfaceOrTrait(nameStr());
1110 } else if (m_parent
.get() && m_parent
->m_extra
->m_instanceCtor
) {
1112 m_extra
.raw()->m_instanceCtor
= m_parent
->m_extra
->m_instanceCtor
;
1113 m_extra
.raw()->m_instanceDtor
= m_parent
->m_extra
->m_instanceDtor
;
1114 m_extra
.raw()->m_builtinODTailSize
= m_parent
->m_extra
->m_builtinODTailSize
;
1115 // XXX: should this be copying over the clsInfo also? Might be broken...
1119 static Func
* findSpecialMethod(Class
* cls
, const StringData
* name
) {
1120 if (!cls
->preClass()->hasMethod(name
)) return nullptr;
1121 Func
* f
= cls
->preClass()->lookupMethod(name
);
1125 f
->setHasPrivateAncestor(false);
1130 s_toString("__toString"),
1131 s_construct("__construct"),
1132 s_destruct("__destruct"),
1133 s_invoke("__invoke"),
1140 s_callStatic("__callStatic"),
1143 void Class::setSpecial() {
1144 m_toString
= lookupMethod(s_toString
.get());
1145 m_dtor
= lookupMethod(s_destruct
.get());
1148 * The invoke method is only cached in the Class for a fast path JIT
1149 * translation. If someone defines a weird __invoke (e.g. as a
1150 * static method), we don't bother caching it here so the translated
1151 * code won't have to check for that case.
1153 * Note that AttrStatic on a closure's __invoke Func* means it is a
1154 * static closure---but the call to __invoke still works as if it
1155 * were a non-static method call---so they are excluded from that
1156 * here. (The closure prologue uninstalls the $this and installs
1157 * the appropriate static context.)
1159 m_invoke
= lookupMethod(s_invoke
.get());
1160 if (m_invoke
&& m_invoke
->isStatic() && !m_invoke
->isClosureBody()) {
1164 auto matchedClassOrIsTrait
= [this](const StringData
* sd
) {
1165 auto func
= lookupMethod(sd
);
1166 if (func
&& (func
->preClass() == m_preClass
.get() ||
1167 func
->preClass()->attrs() & AttrTrait
)) {
1174 // Look for __construct() declared in either this class or a trait
1175 if (matchedClassOrIsTrait(s_construct
.get())) {
1176 auto func
= lookupMethod(m_preClass
->name());
1177 if (func
&& (func
->preClass()->attrs() & AttrTrait
||
1178 m_ctor
->preClass()->attrs() & AttrTrait
)) {
1180 "%s has colliding constructor definitions coming from traits",
1181 m_preClass
->name()->data()
1187 if (!(attrs() & AttrTrait
)) {
1188 // Look for Foo::Foo() declared in this class
1189 if (matchedClassOrIsTrait(m_preClass
->name())) {
1194 // Look for parent constructor other than 86ctor().
1195 if (m_parent
.get() != nullptr &&
1196 m_parent
->m_ctor
->name() != s_86ctor
.get()) {
1197 m_ctor
= m_parent
->m_ctor
;
1201 // Use 86ctor(), since no program-supplied constructor exists
1202 m_ctor
= findSpecialMethod(this, s_86ctor
.get());
1203 assert(m_ctor
&& "class had no user-defined constructor or 86ctor");
1204 assert((m_ctor
->attrs() & ~(AttrBuiltin
|AttrAbstract
|
1205 AttrInterceptable
|AttrMayUseVV
)) ==
1206 (AttrPublic
|AttrNoInjection
|AttrPhpLeafFn
));
1211 inline void raiseIncompat(const PreClass
* implementor
,
1212 const Func
* imeth
) {
1213 const char* name
= imeth
->name()->data();
1214 raise_error("Declaration of %s::%s() must be compatible with "
1216 implementor
->name()->data(), name
,
1217 imeth
->cls()->preClass()->name()->data(), name
);
1220 static bool checkTypeConstraint(const PreClass
* implCls
, const Class
* iface
,
1221 TypeConstraint tc
, TypeConstraint itc
) {
1222 const StringData
* iSelf
;
1223 const StringData
* iParent
;
1224 if (isTrait(iface
)) {
1225 iSelf
= implCls
->name();
1226 iParent
= implCls
->parent();
1228 iSelf
= iface
->name();
1229 iParent
= iface
->parent() ? iface
->parent()->name() : nullptr;
1232 if (tc
.isExtended() || itc
.isExtended()) return true;
1234 if (tc
.isSelf()) tc
= TypeConstraint
{ implCls
->name(), tc
.flags() };
1235 if (tc
.isParent()) tc
= TypeConstraint
{ implCls
->parent(), tc
.flags() };
1236 if (itc
.isSelf()) itc
= TypeConstraint
{ iSelf
, itc
.flags() };
1237 if (itc
.isParent()) itc
= TypeConstraint
{ iParent
, itc
.flags() };
1239 return tc
.compat(itc
);
1242 // Check compatibility vs interface and abstract declarations
1243 void checkDeclarationCompat(const PreClass
* preClass
,
1244 const Func
* func
, const Func
* imeth
) {
1245 bool relaxedCheck
= !RuntimeOption::EnableHipHopSyntax
1247 && !imeth
->unit()->isHHFile();
1249 const Func::ParamInfoVec
& params
= func
->params();
1250 const Func::ParamInfoVec
& iparams
= imeth
->params();
1252 auto const ivariadic
= imeth
->hasVariadicCaptureParam();
1253 if (ivariadic
&& !func
->hasVariadicCaptureParam()) {
1254 raiseIncompat(preClass
, imeth
);
1257 // Verify that meth has at least as many parameters as imeth.
1258 if (func
->numParams() < imeth
->numParams()) {
1259 // This check doesn't require special casing for variadics, because
1260 // it's not ok to turn a variadic function into a non-variadic.
1261 raiseIncompat(preClass
, imeth
);
1263 // Verify that the typehints for meth's parameters are compatible with
1264 // imeth's corresponding parameter typehints.
1265 size_t firstOptional
= 0;
1268 for (; i
< imeth
->numNonVariadicParams(); ++i
) {
1269 auto const& p
= params
[i
];
1270 if (p
.isVariadic()) { raiseIncompat(preClass
, imeth
); }
1271 auto const& ip
= iparams
[i
];
1272 if (!relaxedCheck
) {
1273 // If the interface parameter is a type constant we require the
1274 // implementer to specify a type
1275 if (!p
.userType
&& ip
.typeConstraint
.isTypeConstant()) {
1276 raiseIncompat(preClass
, imeth
);
1279 if (!checkTypeConstraint(preClass
, imeth
->cls(),
1280 p
.typeConstraint
, ip
.typeConstraint
)) {
1281 if (!ip
.typeConstraint
.isTypeVar() &&
1282 !ip
.typeConstraint
.isTypeConstant()) {
1283 raiseIncompat(preClass
, imeth
);
1287 if (!iparams
[i
].hasDefaultValue()) {
1288 // The leftmost of imeth's contiguous trailing optional parameters
1289 // must start somewhere to the right of this parameter (which may
1290 // be the variadic param)
1291 firstOptional
= i
+ 1;
1295 assert(iparams
[iparams
.size() - 1].isVariadic());
1296 assert(params
[params
.size() - 1].isVariadic());
1297 // reffiness of the variadics must match
1298 if (imeth
->byRef(iparams
.size() - 1) !=
1299 func
->byRef(params
.size() - 1)) {
1300 raiseIncompat(preClass
, imeth
);
1303 // To be compatible with a variadic interface, params from the
1304 // variadic onwards must have a compatible typehint
1305 auto const& ivarConstraint
= iparams
[iparams
.size() - 1].typeConstraint
;
1306 if (!ivarConstraint
.isTypeVar()) {
1307 for (; i
< func
->numParams(); ++i
) {
1308 auto const& p
= params
[i
];
1309 if (!checkTypeConstraint(preClass
, imeth
->cls(),
1310 p
.typeConstraint
, ivarConstraint
)) {
1311 raiseIncompat(preClass
, imeth
);
1318 if (!relaxedCheck
) {
1319 // Verify that meth provides defaults, starting with the parameter that
1320 // corresponds to the leftmost of imeth's contiguous trailing optional
1321 // parameters and *not* including any variadic last param (variadics
1322 // don't have any default values).
1323 for (unsigned i
= firstOptional
; i
< func
->numNonVariadicParams(); ++i
) {
1324 if (!params
[i
].hasDefaultValue()) {
1325 raiseIncompat(preClass
, imeth
);
1333 Class::Class(PreClass
* preClass
, Class
* parent
,
1334 std::vector
<ClassPtr
>&& usedTraits
,
1335 unsigned classVecLen
, unsigned funcVecLen
)
1337 , m_preClass(PreClassPtr(preClass
))
1338 , m_classVecLen(always_safe_cast
<decltype(m_classVecLen
)>(classVecLen
))
1339 , m_funcVecLen(always_safe_cast
<decltype(m_funcVecLen
)>(funcVecLen
))
1341 if (usedTraits
.size()) {
1343 m_extra
.raw()->m_usedTraits
= std::move(usedTraits
);
1347 setSpecial(); // must run before setODAttributes
1355 setNativeDataInfo();
1358 // A class is allowed to implement two interfaces that share the same slot if
1359 // we'll fatal trying to define that class, so this has to happen after all
1360 // of those fatals could be thrown.
1361 setInterfaceVtables();
1364 void Class::methodOverrideCheck(const Func
* parentMethod
, const Func
* method
) {
1365 // Skip special methods
1366 if (method
->isGenerated()) return;
1368 if ((parentMethod
->attrs() & AttrFinal
)) {
1369 static StringData
* sd___MockClass
=
1370 makeStaticString("__MockClass");
1371 if (m_preClass
->userAttributes().find(sd___MockClass
) ==
1372 m_preClass
->userAttributes().end()) {
1373 raise_error("Cannot override final method %s::%s()",
1374 m_parent
->name()->data(), parentMethod
->name()->data());
1378 if (method
->attrs() & AttrAbstract
) {
1379 raise_error("Cannot re-declare %sabstract method %s::%s() abstract in "
1381 (parentMethod
->attrs() & AttrAbstract
) ? "" : "non-",
1382 m_parent
->m_preClass
->name()->data(),
1383 parentMethod
->name()->data(), m_preClass
->name()->data());
1386 if ((method
->attrs() & (AttrPublic
| AttrProtected
| AttrPrivate
)) >
1387 (parentMethod
->attrs() & (AttrPublic
| AttrProtected
| AttrPrivate
))) {
1389 "Access level to %s::%s() must be %s (as in class %s) or weaker",
1390 m_preClass
->name()->data(), method
->name()->data(),
1391 attrToVisibilityStr(parentMethod
->attrs()),
1392 m_parent
->name()->data());
1395 if ((method
->attrs() & AttrStatic
) != (parentMethod
->attrs() & AttrStatic
)) {
1396 raise_error("Cannot change %sstatic method %s::%s() to %sstatic in %s",
1397 (parentMethod
->attrs() & AttrStatic
) ? "" : "non-",
1398 parentMethod
->baseCls()->name()->data(),
1399 method
->name()->data(),
1400 (method
->attrs() & AttrStatic
) ? "" : "non-",
1401 m_preClass
->name()->data());
1404 Func
* baseMethod
= parentMethod
->baseCls()->lookupMethod(method
->name());
1405 if (!(method
->attrs() & AttrAbstract
) &&
1406 (baseMethod
->attrs() & AttrAbstract
)) {
1407 checkDeclarationCompat(m_preClass
.get(), method
, baseMethod
);
1411 void Class::setMethods() {
1412 std::vector
<Slot
> parentMethodsWithStaticLocals
;
1413 MethodMapBuilder builder
;
1415 if (m_parent
.get() != nullptr) {
1416 // Copy down the parent's method entries. These may be overridden below.
1417 for (Slot i
= 0; i
< m_parent
->m_methods
.size(); ++i
) {
1418 Func
* f
= m_parent
->getMethod(i
);
1420 if ((f
->attrs() & AttrClone
) ||
1421 (!(f
->attrs() & AttrPrivate
) && f
->hasStaticLocals())) {
1422 // When copying down an entry for a non-private method that has
1423 // static locals, we want to make a copy of the Func so that it
1424 // gets a distinct set of static locals variables. We defer making
1425 // a copy of the parent method until the end because it might get
1427 parentMethodsWithStaticLocals
.push_back(i
);
1429 assert(builder
.size() == i
);
1430 builder
.add(f
->name(), f
);
1434 static_assert(AttrPublic
< AttrProtected
&& AttrProtected
< AttrPrivate
, "");
1435 // Overlay/append this class's public/protected methods onto/to those of the
1437 for (size_t methI
= 0; methI
< m_preClass
->numMethods(); ++methI
) {
1438 Func
* method
= m_preClass
->methods()[methI
];
1439 if (Func::isSpecial(method
->name())) {
1440 if (method
->name() == s_86ctor
.get() ||
1441 method
->name() == s_86sinit
.get() ||
1442 method
->name() == s_86pinit
.get()) {
1444 * we could also skip the cinit function here, but
1445 * that would mean storing it somewhere else.
1451 MethodMapBuilder::iterator it2
= builder
.find(method
->name());
1452 if (it2
!= builder
.end()) {
1453 Func
* parentMethod
= builder
[it2
->second
];
1454 // We should never have null func pointers to deal with
1455 assert(parentMethod
);
1456 methodOverrideCheck(parentMethod
, method
);
1458 Func
* f
= method
->clone(this);
1461 assert(!(f
->attrs() & AttrPrivate
) ||
1462 (parentMethod
->attrs() & AttrPrivate
));
1463 if ((parentMethod
->attrs() & AttrPrivate
) || (f
->attrs() & AttrPrivate
)) {
1466 baseClass
= parentMethod
->baseCls();
1468 f
->setBaseCls(baseClass
);
1469 f
->setHasPrivateAncestor(
1470 parentMethod
->hasPrivateAncestor() ||
1471 (parentMethod
->attrs() & AttrPrivate
));
1472 builder
[it2
->second
] = f
;
1474 // This is the first class that declares the method
1475 Class
* baseClass
= this;
1477 Func
* f
= method
->clone(this);
1479 f
->setBaseCls(baseClass
);
1480 f
->setHasPrivateAncestor(false);
1481 builder
.add(method
->name(), f
);
1485 auto traitsBeginIdx
= builder
.size();
1486 if (m_extra
->m_usedTraits
.size()) {
1487 importTraitMethods(builder
);
1489 auto traitsEndIdx
= builder
.size();
1491 // Make copies of Funcs inherited from the parent class that have
1493 std::vector
<Slot
>::const_iterator it
;
1494 for (it
= parentMethodsWithStaticLocals
.begin();
1495 it
!= parentMethodsWithStaticLocals
.end(); ++it
) {
1496 Func
*& f
= builder
[*it
];
1497 if (f
->cls() != this) {
1498 // Don't update f's m_cls if it doesn't have AttrClone set:
1499 // we're cloning it so that we get a distinct set of static
1500 // locals and a separate translation, not a different context
1502 f
= f
->clone(f
->attrs() & AttrClone
? this : f
->cls());
1504 if (RuntimeOption::EvalPerfDataMap
) {
1505 if (!s_funcIdToClassMap
) {
1506 Lock
l(g_classesMutex
);
1507 if (!s_funcIdToClassMap
) {
1508 s_funcIdToClassMap
= new FuncIdToClassMap
;
1511 FuncIdToClassMap::accessor acc
;
1512 if (!s_funcIdToClassMap
->insert(
1513 acc
, FuncIdToClassMap::value_type(f
->getFuncId(), this))) {
1514 // we only just allocated this id, which is supposedly
1522 if (Class::MethodCreateHook
) {
1523 Class::MethodCreateHook(this, builder
);
1524 // running MethodCreateHook may add methods to builder
1525 traitsEndIdx
= builder
.size();
1529 m_extra
.raw()->m_traitsBeginIdx
= traitsBeginIdx
;
1530 m_extra
.raw()->m_traitsEndIdx
= traitsEndIdx
;
1533 // If class is not abstract, check that all abstract methods have been defined
1534 if (!(attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
))) {
1535 for (Slot i
= 0; i
< builder
.size(); i
++) {
1536 const Func
* meth
= builder
[i
];
1537 if (meth
->attrs() & AttrAbstract
) {
1538 raise_error("Class %s contains abstract method (%s) and "
1539 "must therefore be declared abstract or implement "
1540 "the remaining methods", m_preClass
->name()->data(),
1541 meth
->name()->data());
1546 // If class is abstract final, its static methods should not be abstract
1547 if ((attrs() & (AttrAbstract
| AttrFinal
)) == (AttrAbstract
| AttrFinal
)) {
1548 for (Slot i
= 0; i
< builder
.size(); i
++) {
1549 const Func
* meth
= builder
[i
];
1550 if ((meth
->attrs() & (AttrAbstract
| AttrStatic
))
1551 == (AttrAbstract
| AttrStatic
)) {
1553 "Class %s contains abstract static method (%s) and "
1554 "therefore cannot be declared 'abstract final'",
1555 m_preClass
->name()->data(), meth
->name()->data());
1560 builder
.create(m_methods
);
1561 for (Slot i
= 0; i
< builder
.size(); ++i
) {
1562 builder
[i
]->setMethodSlot(i
);
1564 setFuncVec(builder
);
1567 void Class::setODAttributes() {
1569 if (lookupMethod(s_sleep
.get() )) { m_ODAttrs
|= ObjectData::HasSleep
; }
1570 if (lookupMethod(s_get
.get() )) { m_ODAttrs
|= ObjectData::UseGet
; }
1571 if (lookupMethod(s_set
.get() )) { m_ODAttrs
|= ObjectData::UseSet
; }
1572 if (lookupMethod(s_isset
.get() )) { m_ODAttrs
|= ObjectData::UseIsset
; }
1573 if (lookupMethod(s_unset
.get() )) { m_ODAttrs
|= ObjectData::UseUnset
; }
1574 if (lookupMethod(s_call
.get() )) { m_ODAttrs
|= ObjectData::HasCall
; }
1575 if (lookupMethod(s_clone
.get() )) { m_ODAttrs
|= ObjectData::HasClone
; }
1577 if (m_dtor
== nullptr) m_ODAttrs
|= ObjectData::NoDestructor
;
1579 if ((isBuiltin() && Native::getNativePropHandler(name())) ||
1580 (m_parent
&& m_parent
->hasNativePropHandler())) {
1581 m_ODAttrs
|= ObjectData::HasNativePropHandler
;
1585 void Class::setConstants() {
1586 ConstMap::Builder builder
;
1588 if (m_parent
.get() != nullptr) {
1589 for (Slot i
= 0; i
< m_parent
->m_constants
.size(); ++i
) {
1590 // Copy parent's constants.
1591 builder
.add(m_parent
->m_constants
[i
].name
, m_parent
->m_constants
[i
]);
1595 // Copy in interface constants.
1596 for (int i
= 0, size
= m_interfaces
.size(); i
< size
; ++i
) {
1597 const Class
* iface
= m_interfaces
[i
];
1599 for (Slot slot
= 0; slot
< iface
->m_constants
.size(); ++slot
) {
1600 auto const iConst
= iface
->m_constants
[slot
];
1602 // If you're inheriting a constant with the same name as an existing
1603 // one, they must originate from the same place, unless the constant
1604 // was defined as abstract.
1605 auto const existing
= builder
.find(iConst
.name
);
1607 if (existing
== builder
.end()) {
1608 builder
.add(iConst
.name
, iConst
);
1611 auto& existingConst
= builder
[existing
->second
];
1613 if (iConst
.isType() != existingConst
.isType()) {
1614 raise_error("%s cannot inherit the %sconstant %s from %s, because it "
1615 "was previously inherited as a %sconstant from %s",
1616 m_preClass
->name()->data(),
1617 iConst
.isType() ? "type " : "",
1618 iConst
.name
->data(),
1619 iConst
.cls
->name()->data(),
1620 iConst
.isType() ? "" : "type ",
1621 existingConst
.cls
->name()->data());
1624 if (iConst
.isAbstract()) {
1628 if (existingConst
.isAbstract()) {
1629 existingConst
.cls
= iConst
.cls
;
1630 existingConst
.val
= iConst
.val
;
1634 if (existingConst
.cls
!= iConst
.cls
) {
1635 raise_error("%s cannot inherit the %sconstant %s from %s, because it "
1636 "was previously inherited from %s",
1637 m_preClass
->name()->data(),
1638 iConst
.isType() ? "type " : "",
1639 iConst
.name
->data(),
1640 iConst
.cls
->name()->data(),
1641 existingConst
.cls
->name()->data());
1643 builder
.add(iConst
.name
, iConst
);
1647 for (Slot i
= 0, sz
= m_preClass
->numConstants(); i
< sz
; ++i
) {
1648 const PreClass::Const
* preConst
= &m_preClass
->constants()[i
];
1649 ConstMap::Builder::iterator it2
= builder
.find(preConst
->name());
1650 if (it2
!= builder
.end()) {
1651 auto definingClass
= builder
[it2
->second
].cls
;
1652 // Forbid redefining constants from interfaces, but not superclasses.
1653 // Constants from interfaces implemented by superclasses can be
1655 if (definingClass
->attrs() & AttrInterface
) {
1656 for (auto interface
: declInterfaces()) {
1657 if (interface
->hasConstant(preConst
->name()) ||
1658 interface
->hasTypeConstant(preConst
->name())) {
1659 raise_error("Cannot override previously defined %sconstant "
1661 builder
[it2
->second
].isType() ? "type " : "",
1662 builder
[it2
->second
].cls
->name()->data(),
1663 preConst
->name()->data(),
1664 m_preClass
->name()->data());
1669 if (preConst
->isAbstract() &&
1670 !builder
[it2
->second
].isAbstract()) {
1671 raise_error("Cannot re-declare as abstract previously defined "
1672 "%sconstant %s::%s in %s",
1673 builder
[it2
->second
].isType() ? "type " : "",
1674 builder
[it2
->second
].cls
->name()->data(),
1675 preConst
->name()->data(),
1676 m_preClass
->name()->data());
1679 if (preConst
->isType() != builder
[it2
->second
].isType()) {
1680 raise_error("Cannot re-declare as a %sconstant previously defined "
1681 "%sconstant %s::%s in %s",
1682 preConst
->isType() ? "type " : "",
1683 preConst
->isType() ? "" : "type ",
1684 builder
[it2
->second
].cls
->name()->data(),
1685 preConst
->name()->data(),
1686 m_preClass
->name()->data());
1688 builder
[it2
->second
].cls
= this;
1689 builder
[it2
->second
].val
= preConst
->val();
1693 constant
.cls
= this;
1694 constant
.name
= preConst
->name();
1695 constant
.val
= preConst
->val();
1696 builder
.add(preConst
->name(), constant
);
1700 // If class is not abstract, all abstract constants should have been
1702 if (!(attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
))) {
1703 for (Slot i
= 0; i
< builder
.size(); i
++) {
1704 const Const
& constant
= builder
[i
];
1705 if (constant
.isAbstract()) {
1706 raise_error("Class %s contains abstract %sconstant (%s) and "
1707 "must therefore be declared abstract or define "
1708 "the remaining constants",
1709 m_preClass
->name()->data(),
1710 constant
.isType() ? "type " : "",
1711 constant
.name
->data());
1716 // If class is abstract final, its constants should not be abstract
1718 (attrs() & (AttrAbstract
| AttrFinal
)) == (AttrAbstract
| AttrFinal
)) {
1719 for (Slot i
= 0; i
< builder
.size(); i
++) {
1720 const Const
& constant
= builder
[i
];
1721 if (constant
.isAbstract()) {
1723 "Class %s contains abstract %sconstant (%s) and "
1724 "therefore cannot be declared 'abstract final'",
1725 m_preClass
->name()->data(),
1726 constant
.isType() ? "type " : "",
1727 constant
.name
->data());
1732 m_constants
.create(builder
);
1735 static void copyDeepInitAttr(const PreClass::Prop
* pclsProp
,
1736 Class::Prop
* clsProp
) {
1737 if (pclsProp
->attrs() & AttrDeepInit
) {
1738 clsProp
->attrs
= (Attr
)(clsProp
->attrs
| AttrDeepInit
);
1740 clsProp
->attrs
= (Attr
)(clsProp
->attrs
& ~AttrDeepInit
);
1744 void Class::setProperties() {
1745 int numInaccessible
= 0;
1746 PropMap::Builder curPropMap
;
1747 SPropMap::Builder curSPropMap
;
1748 m_hasDeepInitProps
= false;
1749 Slot traitOffset
= 0;
1751 if (m_parent
.get() != nullptr) {
1752 // m_hasDeepInitProps indicates if there are properties that require
1753 // deep initialization. Note there are cases where m_hasDeepInitProps is
1754 // true but none of the properties require deep initialization; this can
1755 // happen if a derived class redeclares a public or protected property
1756 // from an ancestor class. We still get correct behavior in these cases,
1757 // so it works out okay.
1758 m_hasDeepInitProps
= m_parent
->m_hasDeepInitProps
;
1759 for (auto const& parentProp
: m_parent
->declProperties()) {
1760 // Copy parent's declared property. Protected properties may be
1761 // weakened to public below, but otherwise, the parent's properties
1762 // will stay the same for this class.
1764 prop
.cls
= parentProp
.cls
;
1765 prop
.mangledName
= parentProp
.mangledName
;
1766 prop
.originalMangledName
= parentProp
.originalMangledName
;
1767 prop
.attrs
= parentProp
.attrs
;
1768 prop
.docComment
= parentProp
.docComment
;
1769 prop
.typeConstraint
= parentProp
.typeConstraint
;
1770 prop
.name
= parentProp
.name
;
1771 prop
.repoAuthType
= parentProp
.repoAuthType
;
1772 // Temporarily assign parent properties' indexes to their additive
1773 // inverses minus one. After assigning current properties' indexes, we
1774 // will use these negative indexes to assign new indexes to parent
1775 // properties that haven't been overlayed.
1776 prop
.idx
= -parentProp
.idx
- 1;
1777 if (traitOffset
< -prop
.idx
) {
1778 traitOffset
= -prop
.idx
;
1780 if (!(parentProp
.attrs
& AttrPrivate
)) {
1781 curPropMap
.add(prop
.name
, prop
);
1784 curPropMap
.addUnnamed(prop
);
1787 m_declPropInit
= m_parent
->m_declPropInit
;
1788 for (auto const& parentProp
: m_parent
->staticProperties()) {
1789 if (parentProp
.attrs
& AttrPrivate
) continue;
1791 // Alias parent's static property.
1793 sProp
.name
= parentProp
.name
;
1794 sProp
.attrs
= parentProp
.attrs
;
1795 sProp
.typeConstraint
= parentProp
.typeConstraint
;
1796 sProp
.docComment
= parentProp
.docComment
;
1797 sProp
.cls
= parentProp
.cls
;
1798 sProp
.idx
= -parentProp
.idx
- 1;
1799 if (traitOffset
< -sProp
.idx
) {
1800 traitOffset
= -sProp
.idx
;
1802 tvWriteUninit(&sProp
.val
);
1803 curSPropMap
.add(sProp
.name
, sProp
);
1807 Slot traitIdx
= m_preClass
->numProperties();
1808 if (RuntimeOption::RepoAuthoritative
) {
1809 for (auto const& traitName
: m_preClass
->usedTraits()) {
1810 Class
* classPtr
= Unit::loadClass(traitName
);
1811 traitIdx
-= classPtr
->m_declProperties
.size() +
1812 classPtr
->m_staticProperties
.size();
1816 static_assert(AttrPublic
< AttrProtected
&& AttrProtected
< AttrPrivate
, "");
1817 for (Slot slot
= 0; slot
< m_preClass
->numProperties(); ++slot
) {
1818 const PreClass::Prop
* preProp
= &m_preClass
->properties()[slot
];
1820 if (!(preProp
->attrs() & AttrStatic
)) {
1821 // Overlay/append this class's protected and public properties onto/to
1822 // those of the parent, and append this class's private properties.
1823 // Append order doesn't matter here (unlike in setMethods()).
1824 // Prohibit static-->non-static redeclaration.
1825 SPropMap::Builder::iterator it2
= curSPropMap
.find(preProp
->name());
1826 if (it2
!= curSPropMap
.end()) {
1827 raise_error("Cannot redeclare static %s::$%s as non-static %s::$%s",
1828 curSPropMap
[it2
->second
].cls
->name()->data(),
1829 preProp
->name()->data(), m_preClass
->name()->data(),
1830 preProp
->name()->data());
1832 // Get parent's equivalent property, if one exists.
1833 const Prop
* parentProp
= nullptr;
1834 if (m_parent
.get() != nullptr) {
1835 Slot id
= m_parent
->m_declProperties
.findIndex(preProp
->name());
1836 if (id
!= kInvalidSlot
) {
1837 parentProp
= &m_parent
->m_declProperties
[id
];
1840 // Prohibit strengthening.
1842 && (preProp
->attrs() & (AttrPublic
|AttrProtected
|AttrPrivate
))
1843 > (parentProp
->attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
))) {
1845 "Access level to %s::$%s() must be %s (as in class %s) or weaker",
1846 m_preClass
->name()->data(), preProp
->name()->data(),
1847 attrToVisibilityStr(parentProp
->attrs
),
1848 m_parent
->name()->data());
1850 if (preProp
->attrs() & AttrDeepInit
) {
1851 m_hasDeepInitProps
= true;
1853 switch (preProp
->attrs() & (AttrPublic
|AttrProtected
|AttrPrivate
)) {
1855 // Append a new private property.
1857 prop
.name
= preProp
->name();
1858 prop
.mangledName
= preProp
->mangledName();
1859 prop
.originalMangledName
= preProp
->mangledName();
1860 prop
.attrs
= preProp
->attrs();
1861 // This is the first class to declare this property
1863 prop
.typeConstraint
= preProp
->typeConstraint();
1864 prop
.docComment
= preProp
->docComment();
1865 prop
.repoAuthType
= preProp
->repoAuthType();
1866 if (slot
< traitIdx
) {
1869 prop
.idx
= slot
+ m_preClass
->numProperties() + traitOffset
;
1871 curPropMap
.add(preProp
->name(), prop
);
1872 m_declPropInit
.push_back(m_preClass
->lookupProp(preProp
->name())
1876 case AttrProtected
: {
1877 // Check whether a superclass has already declared this protected
1879 PropMap::Builder::iterator it2
= curPropMap
.find(preProp
->name());
1880 if (it2
!= curPropMap
.end()) {
1881 auto& prop
= curPropMap
[it2
->second
];
1882 assert((prop
.attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
)) ==
1885 prop
.docComment
= preProp
->docComment();
1886 if (slot
< traitIdx
) {
1889 prop
.idx
= slot
+ m_preClass
->numProperties() + traitOffset
;
1891 const TypedValue
& tv
= m_preClass
->lookupProp(preProp
->name())->val();
1892 TypedValueAux
& tvaux
= m_declPropInit
[it2
->second
];
1893 tvaux
.m_data
= tv
.m_data
;
1894 tvaux
.m_type
= tv
.m_type
;
1895 copyDeepInitAttr(preProp
, &prop
);
1898 // Append a new protected property.
1900 prop
.name
= preProp
->name();
1901 prop
.mangledName
= preProp
->mangledName();
1902 prop
.originalMangledName
= preProp
->mangledName();
1903 prop
.attrs
= preProp
->attrs();
1904 prop
.typeConstraint
= preProp
->typeConstraint();
1905 // This is the first class to declare this property
1907 prop
.docComment
= preProp
->docComment();
1908 prop
.repoAuthType
= preProp
->repoAuthType();
1909 if (slot
< traitIdx
) {
1912 prop
.idx
= slot
+ m_preClass
->numProperties() + traitOffset
;
1914 curPropMap
.add(preProp
->name(), prop
);
1915 m_declPropInit
.push_back(m_preClass
->lookupProp(preProp
->name())
1920 // Check whether a superclass has already declared this as a
1921 // protected/public property.
1922 auto it2
= curPropMap
.find(preProp
->name());
1923 if (it2
!= curPropMap
.end()) {
1924 auto& prop
= curPropMap
[it2
->second
];
1926 prop
.docComment
= preProp
->docComment();
1927 if ((prop
.attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
))
1929 // Weaken protected property to public.
1930 prop
.mangledName
= preProp
->mangledName();
1931 prop
.originalMangledName
= preProp
->mangledName();
1932 prop
.attrs
= Attr(prop
.attrs
^ (AttrProtected
|AttrPublic
));
1933 prop
.typeConstraint
= preProp
->typeConstraint();
1935 if (slot
< traitIdx
) {
1938 prop
.idx
= slot
+ m_preClass
->numProperties() + traitOffset
;
1940 auto const& tv
= m_preClass
->lookupProp(preProp
->name())->val();
1941 TypedValueAux
& tvaux
= m_declPropInit
[it2
->second
];
1942 tvaux
.m_data
= tv
.m_data
;
1943 tvaux
.m_type
= tv
.m_type
;
1944 copyDeepInitAttr(preProp
, &prop
);
1947 // Append a new public property.
1949 prop
.name
= preProp
->name();
1950 prop
.mangledName
= preProp
->mangledName();
1951 prop
.originalMangledName
= preProp
->mangledName();
1952 prop
.attrs
= preProp
->attrs();
1953 prop
.typeConstraint
= preProp
->typeConstraint();
1954 // This is the first class to declare this property
1956 prop
.docComment
= preProp
->docComment();
1957 prop
.repoAuthType
= preProp
->repoAuthType();
1958 if (slot
< traitIdx
) {
1961 prop
.idx
= slot
+ m_preClass
->numProperties() + traitOffset
;
1963 curPropMap
.add(preProp
->name(), prop
);
1964 m_declPropInit
.push_back(m_preClass
->lookupProp(preProp
->name())
1968 default: assert(false);
1970 } else { // Static property.
1971 // Prohibit non-static-->static redeclaration.
1972 auto const it2
= curPropMap
.find(preProp
->name());
1973 if (it2
!= curPropMap
.end()) {
1974 auto& prop
= curPropMap
[it2
->second
];
1975 raise_error("Cannot redeclare non-static %s::$%s as static %s::$%s",
1976 prop
.cls
->name()->data(),
1977 preProp
->name()->data(),
1978 m_preClass
->name()->data(),
1979 preProp
->name()->data());
1981 // Get parent's equivalent property, if one exists.
1982 auto const it3
= curSPropMap
.find(preProp
->name());
1983 Slot sPropInd
= kInvalidSlot
;
1984 // Prohibit strengthening.
1985 if (it3
!= curSPropMap
.end()) {
1986 const SProp
& parentSProp
= curSPropMap
[it3
->second
];
1987 if ((preProp
->attrs() & (AttrPublic
|AttrProtected
|AttrPrivate
))
1988 > (parentSProp
.attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
))) {
1990 "Access level to %s::$%s() must be %s (as in class %s) or weaker",
1991 m_preClass
->name()->data(), preProp
->name()->data(),
1992 attrToVisibilityStr(parentSProp
.attrs
),
1993 m_parent
->name()->data());
1995 sPropInd
= it3
->second
;
1997 // Create a new property, or overlay ancestor's property if one exists.
1998 if (sPropInd
== kInvalidSlot
) {
2000 sProp
.name
= preProp
->name();
2001 sPropInd
= curSPropMap
.size();
2002 curSPropMap
.add(sProp
.name
, sProp
);
2004 // Finish initializing.
2005 auto& sProp
= curSPropMap
[sPropInd
];
2006 sProp
.attrs
= preProp
->attrs();
2007 sProp
.typeConstraint
= preProp
->typeConstraint();
2008 sProp
.docComment
= preProp
->docComment();
2010 sProp
.val
= m_preClass
->lookupProp(preProp
->name())->val();
2011 sProp
.repoAuthType
= preProp
->repoAuthType();
2012 if (slot
< traitIdx
) {
2015 sProp
.idx
= slot
+ m_preClass
->numProperties() + traitOffset
;
2020 // After assigning indexes for current properties, we reassign indexes to
2021 // parent properties that haven't been overlayed to make sure that they
2022 // are greater than those of current properties.
2023 int idxOffset
= m_preClass
->numProperties() - 1;
2024 int curIdx
= idxOffset
;
2025 for (Slot slot
= 0; slot
< curPropMap
.size(); ++slot
) {
2026 auto& prop
= curPropMap
[slot
];
2028 prop
.idx
= idxOffset
- prop
.idx
;
2029 if (curIdx
< prop
.idx
) {
2034 for (Slot slot
= 0; slot
< curSPropMap
.size(); ++slot
) {
2035 auto& sProp
= curSPropMap
[slot
];
2036 if (sProp
.idx
< 0) {
2037 sProp
.idx
= idxOffset
- sProp
.idx
;
2038 if (curIdx
< sProp
.idx
) {
2044 importTraitProps(curIdx
+ 1, curPropMap
, curSPropMap
);
2046 m_declProperties
.create(curPropMap
);
2047 m_staticProperties
.create(curSPropMap
);
2049 m_sPropCache
= (rds::Link
<TypedValue
>*)
2050 malloc(numStaticProperties() * sizeof(*m_sPropCache
));
2051 for (unsigned i
= 0, n
= numStaticProperties(); i
< n
; ++i
) {
2052 new (&m_sPropCache
[i
]) rds::Link
<TypedValue
>(rds::kInvalidHandle
);
2055 m_declPropNumAccessible
= m_declProperties
.size() - numInaccessible
;
2058 bool Class::compatibleTraitPropInit(TypedValue
& tv1
, TypedValue
& tv2
) {
2059 if (tv1
.m_type
!= tv2
.m_type
) return false;
2061 switch (tv1
.m_type
) {
2068 case KindOfStaticString
:
2070 return same(tvAsVariant(&tv1
), tvAsVariant(&tv2
));
2075 case KindOfResource
:
2085 void Class::importTraitInstanceProp(Class
* trait
,
2087 TypedValue
& traitPropVal
,
2088 const int idxOffset
,
2089 PropMap::Builder
& curPropMap
) {
2090 auto prevIt
= curPropMap
.find(traitProp
.name
);
2092 if (prevIt
== curPropMap
.end()) {
2093 // New prop, go ahead and add it
2094 Prop prop
= traitProp
;
2095 prop
.cls
= this; // set current class as the first declaring prop
2096 // private props' mangled names contain the class name, so regenerate them
2097 if (prop
.attrs
& AttrPrivate
) {
2098 prop
.mangledName
= PreClass::manglePropName(m_preClass
->name(),
2102 if (prop
.attrs
& AttrDeepInit
) {
2103 m_hasDeepInitProps
= true;
2105 prop
.idx
+= idxOffset
;
2106 curPropMap
.add(prop
.name
, prop
);
2107 m_declPropInit
.push_back(traitPropVal
);
2109 // Redeclared prop, make sure it matches previous declarations
2110 auto& prevProp
= curPropMap
[prevIt
->second
];
2111 auto& prevPropVal
= m_declPropInit
[prevIt
->second
];
2112 if (prevProp
.attrs
!= traitProp
.attrs
||
2113 !compatibleTraitPropInit(prevPropVal
, traitPropVal
)) {
2114 raise_error("trait declaration of property '%s' is incompatible with "
2115 "previous declaration", traitProp
.name
->data());
2120 void Class::importTraitStaticProp(Class
* trait
,
2122 const int idxOffset
,
2123 PropMap::Builder
& curPropMap
,
2124 SPropMap::Builder
& curSPropMap
) {
2125 // Check if prop already declared as non-static
2126 if (curPropMap
.find(traitProp
.name
) != curPropMap
.end()) {
2127 raise_error("trait declaration of property '%s' is incompatible with "
2128 "previous declaration", traitProp
.name
->data());
2131 auto prevIt
= curSPropMap
.find(traitProp
.name
);
2132 if (prevIt
== curSPropMap
.end()) {
2133 // New prop, go ahead and add it
2134 SProp prop
= traitProp
;
2135 prop
.cls
= this; // set current class as the first declaring prop
2136 prop
.idx
+= idxOffset
;
2137 curSPropMap
.add(prop
.name
, prop
);
2139 // Redeclared prop, make sure it matches previous declaration
2140 auto& prevProp
= curSPropMap
[prevIt
->second
];
2141 TypedValue prevPropVal
;
2142 if (prevProp
.cls
== this) {
2143 // If this static property was declared by this class, we can get the
2144 // initial value directly from its value.
2145 prevPropVal
= prevProp
.val
;
2147 // If this static property was declared in a parent class, its value will
2148 // be KindOfUninit, and we'll need to consult the appropriate parent class
2149 // to get the initial value.
2150 auto const& prevSProps
= prevProp
.cls
->m_staticProperties
;
2152 auto prevPropInd
= prevSProps
.findIndex(prevProp
.name
);
2153 assert(prevPropInd
!= kInvalidSlot
);
2155 prevPropVal
= prevSProps
[prevPropInd
].val
;
2157 if (prevProp
.attrs
!= traitProp
.attrs
||
2158 !compatibleTraitPropInit(traitProp
.val
, prevPropVal
)) {
2159 raise_error("trait declaration of property '%s' is incompatible with "
2160 "previous declaration", traitProp
.name
->data());
2162 prevProp
.cls
= this;
2163 prevProp
.val
= prevPropVal
;
2167 void Class::importTraitProps(int idxOffset
,
2168 PropMap::Builder
& curPropMap
,
2169 SPropMap::Builder
& curSPropMap
) {
2170 if (attrs() & AttrNoExpandTrait
) return;
2171 for (auto const& t
: m_extra
->m_usedTraits
) {
2172 auto trait
= t
.get();
2174 // instance properties
2175 for (Slot p
= 0; p
< trait
->m_declProperties
.size(); p
++) {
2176 auto& traitProp
= trait
->m_declProperties
[p
];
2177 auto& traitPropVal
= trait
->m_declPropInit
[p
];
2178 importTraitInstanceProp(trait
, traitProp
, traitPropVal
, idxOffset
,
2182 // static properties
2183 for (Slot p
= 0; p
< trait
->m_staticProperties
.size(); ++p
) {
2184 auto& traitProp
= trait
->m_staticProperties
[p
];
2185 importTraitStaticProp(trait
, traitProp
, idxOffset
, curPropMap
,
2189 idxOffset
+= trait
->m_declProperties
.size() +
2190 trait
->m_staticProperties
.size();
2194 void Class::addTraitPropInitializers(std::vector
<const Func
*>& thisInitVec
,
2196 if (attrs() & AttrNoExpandTrait
) return;
2197 for (auto const& t
: m_extra
->m_usedTraits
) {
2198 Class
* trait
= t
.get();
2199 auto& traitInitVec
= staticProps
? trait
->m_sinitVec
: trait
->m_pinitVec
;
2200 // Insert trait's 86[ps]init into the current class, avoiding repetitions.
2201 for (unsigned m
= 0; m
< traitInitVec
.size(); m
++) {
2202 // Clone 86[ps]init methods, and set the class to the current class.
2203 // This allows 86[ps]init to determine the property offset for the
2204 // initializer array corectly.
2205 Func
*f
= traitInitVec
[m
]->clone(this);
2207 f
->setBaseCls(this);
2208 f
->setHasPrivateAncestor(false);
2209 thisInitVec
.push_back(f
);
2214 void Class::setInitializers() {
2215 std::vector
<const Func
*> pinits
;
2216 std::vector
<const Func
*> sinits
;
2218 if (m_parent
.get() != nullptr) {
2219 // Copy parent's 86pinit() vector, so that the 86pinit() methods can be
2220 // called in reverse order without any search/recursion during
2222 pinits
.assign(m_parent
->m_pinitVec
.begin(), m_parent
->m_pinitVec
.end());
2225 // This class only has a __[ps]init() method if it's needed. Append to the
2226 // vectors of __[ps]init() methods, so that reverse iteration of the vectors
2227 // runs this class's __[ps]init() first, in case multiple classes in the
2228 // hierarchy initialize the same property.
2229 const Func
* meth86pinit
= findSpecialMethod(this, s_86pinit
.get());
2230 if (meth86pinit
!= nullptr) {
2231 pinits
.push_back(meth86pinit
);
2233 addTraitPropInitializers(pinits
, false);
2234 const Func
* sinit
= findSpecialMethod(this, s_86sinit
.get());
2236 sinits
.push_back(sinit
);
2238 addTraitPropInitializers(sinits
, true);
2240 m_pinitVec
= pinits
;
2241 m_sinitVec
= sinits
;
2243 m_needInitialization
= (m_pinitVec
.size() > 0 ||
2244 m_staticProperties
.size() > 0);
2246 // The __init__ method gets special treatment
2247 static StringData
* s_init__
= makeStaticString("__init__");
2248 auto method
= lookupMethod(s_init__
);
2249 m_callsCustomInstanceInit
= method
&& method
->isBuiltin();
2252 void Class::checkInterfaceConstraints() {
2253 if (UNLIKELY(m_interfaces
.contains(String("Iterator").get()) &&
2254 m_interfaces
.contains(String("IteratorAggregate").get()))) {
2255 raise_error("Class %s cannot implement both IteratorAggregate and Iterator"
2256 " at the same time", name()->data());
2260 // Checks if interface methods are OK:
2261 // - there's no requirement if this is a trait, interface, or abstract class
2262 // - a non-abstract class must implement all methods from interfaces it
2263 // declares to implement (either directly or indirectly), arity must be
2264 // compatible (at least as many parameters, additional parameters must have
2265 // defaults), and typehints must be compatible
2266 void Class::checkInterfaceMethods() {
2267 for (int i
= 0, size
= m_interfaces
.size(); i
< size
; i
++) {
2268 const Class
* iface
= m_interfaces
[i
];
2270 for (size_t m
= 0; m
< iface
->m_methods
.size(); m
++) {
2271 Func
* imeth
= iface
->getMethod(m
);
2272 const StringData
* methName
= imeth
->name();
2274 // Skip special methods
2275 if (Func::isSpecial(methName
)) continue;
2277 Func
* meth
= lookupMethod(methName
);
2279 if (attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
)) {
2280 if (meth
== nullptr) {
2281 // Skip unimplemented method.
2285 // Verify that method is not abstract within concrete class.
2286 if (meth
== nullptr || (meth
->attrs() & AttrAbstract
)) {
2287 raise_error("Class %s contains abstract method (%s) and "
2288 "must therefore be declared abstract or implement "
2289 "the remaining methods", name()->data(),
2293 bool ifaceStaticMethod
= imeth
->attrs() & AttrStatic
;
2294 bool classStaticMethod
= meth
->attrs() & AttrStatic
;
2295 if (classStaticMethod
!= ifaceStaticMethod
) {
2296 raise_error("Cannot make %sstatic method %s::%s() %sstatic "
2298 ifaceStaticMethod
? "" : "non-",
2299 iface
->m_preClass
->name()->data(), methName
->data(),
2300 classStaticMethod
? "" : "non-",
2301 m_preClass
->name()->data());
2303 if ((imeth
->attrs() & AttrPublic
) &&
2304 !(meth
->attrs() & AttrPublic
)) {
2305 raise_error("Access level to %s::%s() must be public "
2306 "(as in interface %s)", m_preClass
->name()->data(),
2307 methName
->data(), iface
->m_preClass
->name()->data());
2309 checkDeclarationCompat(m_preClass
.get(), meth
, imeth
);
2315 * Look up the interfaces implemented by traits used by the class, and add them
2316 * to the provided builder.
2318 void Class::addInterfacesFromUsedTraits(InterfaceMap::Builder
& builder
) const {
2320 for (auto const& trait
: m_extra
->m_usedTraits
) {
2321 int numIfcs
= trait
->m_interfaces
.size();
2323 for (int i
= 0; i
< numIfcs
; i
++) {
2324 auto interface
= trait
->m_interfaces
[i
];
2325 if (builder
.find(interface
->name()) == builder
.end()) {
2326 builder
.add(interface
->name(), interface
);
2332 const StaticString
s_Stringish("Stringish");
2334 void Class::setInterfaces() {
2335 InterfaceMap::Builder interfacesBuilder
;
2336 if (m_parent
.get() != nullptr) {
2337 int size
= m_parent
->m_interfaces
.size();
2338 for (int i
= 0; i
< size
; i
++) {
2339 auto interface
= m_parent
->m_interfaces
[i
];
2340 interfacesBuilder
.add(interface
->name(), interface
);
2344 std::vector
<ClassPtr
> declInterfaces
;
2346 for (auto it
= m_preClass
->interfaces().begin();
2347 it
!= m_preClass
->interfaces().end(); ++it
) {
2348 auto cp
= Unit::loadClass(*it
);
2349 if (cp
== nullptr) {
2350 raise_error("Undefined interface: %s", (*it
)->data());
2352 if (!(cp
->attrs() & AttrInterface
)) {
2353 raise_error("%s cannot implement %s - it is not an interface",
2354 m_preClass
->name()->data(), cp
->name()->data());
2356 declInterfaces
.push_back(ClassPtr(cp
));
2357 if (interfacesBuilder
.find(cp
->name()) == interfacesBuilder
.end()) {
2358 interfacesBuilder
.add(cp
->name(), LowPtr
<Class
>(cp
));
2360 int size
= cp
->m_interfaces
.size();
2361 for (int i
= 0; i
< size
; i
++) {
2362 auto interface
= cp
->m_interfaces
[i
];
2363 interfacesBuilder
.find(interface
->name());
2364 if (interfacesBuilder
.find(interface
->name()) ==
2365 interfacesBuilder
.end()) {
2366 interfacesBuilder
.add(interface
->name(), interface
);
2371 m_numDeclInterfaces
= declInterfaces
.size();
2372 m_declInterfaces
.reset(new ClassPtr
[declInterfaces
.size()]);
2373 std::copy(std::begin(declInterfaces
),
2374 std::end(declInterfaces
),
2375 m_declInterfaces
.get());
2377 addInterfacesFromUsedTraits(interfacesBuilder
);
2380 auto const present
= interfacesBuilder
.find(s_Stringish
.get());
2381 if (present
== interfacesBuilder
.end()
2382 && (!(attrs() & AttrInterface
) ||
2383 !m_preClass
->name()->isame(s_Stringish
.get()))) {
2384 Class
* stringish
= Unit::lookupClass(s_Stringish
.get());
2385 assert(stringish
!= nullptr);
2386 assert((stringish
->attrs() & AttrInterface
));
2387 interfacesBuilder
.add(stringish
->name(), LowPtr
<Class
>(stringish
));
2391 m_interfaces
.create(interfacesBuilder
);
2392 checkInterfaceConstraints();
2393 checkInterfaceMethods();
2396 void Class::setInterfaceVtables() {
2397 // We only need to set interface vtables for classes that can be instantiated
2398 // and implement more than 0 interfaces.
2399 if (!RuntimeOption::RepoAuthoritative
||
2400 !isNormalClass(this) || isAbstract(this) || m_interfaces
.empty()) return;
2402 size_t totalMethods
= 0;
2404 for (auto iface
: m_interfaces
.range()) {
2405 auto const slot
= iface
->preClass()->ifaceVtableSlot();
2406 if (slot
== kInvalidSlot
) continue;
2408 maxSlot
= std::max(maxSlot
, slot
);
2409 totalMethods
+= iface
->numMethods();
2412 const size_t nVtables
= maxSlot
+ 1;
2413 auto const vtableVecSz
= nVtables
* sizeof(VtableVecSlot
);
2414 auto const memSz
= vtableVecSz
+ totalMethods
* sizeof(LowPtr
<Func
>);
2415 auto const mem
= static_cast<char*>(low_malloc(memSz
));
2418 ITRACE(3, "Setting interface vtables for class {}. "
2419 "{} interfaces, {} vtable slots, {} total methods\n",
2420 name()->data(), m_interfaces
.size(), nVtables
, totalMethods
);
2421 Trace::Indent indent
;
2423 auto const vtableVec
= reinterpret_cast<VtableVecSlot
*>(cursor
);
2424 cursor
+= vtableVecSz
;
2425 m_vtableVecLen
= always_safe_cast
<decltype(m_vtableVecLen
)>(nVtables
);
2426 m_vtableVec
= vtableVec
;
2427 memset(vtableVec
, 0, vtableVecSz
);
2429 for (auto iface
: m_interfaces
.range()) {
2430 auto const slot
= iface
->preClass()->ifaceVtableSlot();
2431 if (slot
== kInvalidSlot
) continue;
2432 ITRACE(3, "{} @ slot {}\n", iface
->name()->data(), slot
);
2433 Trace::Indent indent
;
2434 always_assert(slot
< nVtables
);
2436 auto const nMethods
= iface
->numMethods();
2437 auto const vtable
= reinterpret_cast<LowPtr
<Func
>*>(cursor
);
2438 cursor
+= nMethods
* sizeof(LowPtr
<Func
>);
2439 always_assert(vtableVec
[slot
].vtable
== nullptr);
2440 vtableVec
[slot
].vtable
= vtable
;
2441 vtableVec
[slot
].iface
= iface
;
2443 for (size_t i
= 0; i
< nMethods
; ++i
) {
2444 auto ifunc
= iface
->getMethod(i
);
2445 auto func
= lookupMethod(ifunc
->name());
2446 ITRACE(3, "{}:{} @ slot {}\n", ifunc
->name()->data(), func
, i
);
2447 always_assert(func
|| Func::isSpecial(ifunc
->name()));
2452 always_assert(cursor
== mem
+ memSz
);
2455 void Class::setRequirements() {
2456 RequirementMap::Builder reqBuilder
;
2458 if (m_parent
.get() != nullptr) {
2459 for (auto const& req
: m_parent
->allRequirements().range()) {
2460 reqBuilder
.add(req
->name(), req
);
2463 for (auto const& iface
: m_interfaces
.range()) {
2464 for (auto const& req
: iface
->allRequirements().range()) {
2465 reqBuilder
.add(req
->name(), req
);
2468 for (auto const& ut
: m_extra
->m_usedTraits
) {
2469 for (auto const& req
: ut
->allRequirements().range()) {
2470 reqBuilder
.add(req
->name(), req
);
2474 if (attrs() & AttrTrait
) {
2475 // Check that requirements are semantically valid.
2476 for (auto const& req
: m_preClass
->requirements()) {
2477 auto const reqName
= req
.name();
2478 auto const reqCls
= Unit::loadClass(reqName
);
2480 raise_error("%s '%s' required by trait '%s' cannot be loaded",
2481 req
.is_extends() ? "Class" : "Interface",
2483 m_preClass
->name()->data());
2486 if (req
.is_extends()) {
2487 if (reqCls
->attrs() & (AttrTrait
| AttrInterface
| AttrFinal
)) {
2488 raise_error(Strings::TRAIT_BAD_REQ_EXTENDS
,
2489 m_preClass
->name()->data(),
2494 assert(req
.is_implements());
2495 if (!(reqCls
->attrs() & AttrInterface
)) {
2496 raise_error(Strings::TRAIT_BAD_REQ_IMPLEMENTS
,
2497 m_preClass
->name()->data(),
2503 reqBuilder
.add(reqName
, &req
);
2505 } else if (attrs() & AttrInterface
) {
2506 // Check that requirements are semantically valid
2507 for (auto const& req
: m_preClass
->requirements()) {
2508 auto const reqName
= req
.name();
2509 auto const reqCls
= Unit::loadClass(reqName
);
2511 raise_error("'%s' required by interface '%s' cannot be loaded",
2513 m_preClass
->name()->data());
2516 assert(req
.is_extends());
2517 if (reqCls
->attrs() & (AttrTrait
| AttrInterface
| AttrFinal
)) {
2518 raise_error("Interface '%s' requires extension of '%s', but %s "
2519 "is not an extendable class",
2520 m_preClass
->name()->data(),
2524 reqBuilder
.add(reqName
, &req
);
2526 } else if (RuntimeOption::RepoAuthoritative
) {
2527 // The flattening of traits may result in requirements migrating from
2528 // the trait's declaration into that of the "using" class.
2529 for (auto const& req
: m_preClass
->requirements()) {
2530 auto const reqName
= req
.name();
2531 auto const reqCls
= Unit::loadClass(reqName
);
2533 raise_error("%s '%s' required by trait '%s' cannot be loaded",
2534 req
.is_extends() ? "Class" : "Interface",
2536 m_preClass
->name()->data());
2539 if (req
.is_extends()) {
2540 if (reqCls
->attrs() & (AttrTrait
| AttrInterface
| AttrFinal
)) {
2541 raise_error(Strings::TRAIT_BAD_REQ_EXTENDS
,
2542 m_preClass
->name()->data(),
2547 assert(req
.is_implements());
2548 if (!(reqCls
->attrs() & AttrInterface
)) {
2549 raise_error(Strings::TRAIT_BAD_REQ_IMPLEMENTS
,
2550 m_preClass
->name()->data(),
2555 reqBuilder
.add(reqName
, &req
);
2559 m_requirements
.create(reqBuilder
);
2560 checkRequirementConstraints();
2563 void Class::setEnumType() {
2564 if (attrs() & AttrEnum
) {
2565 m_enumBaseTy
= m_preClass
->enumBaseTy().underlyingDataTypeResolved();
2567 // Make sure we've loaded a valid underlying type.
2569 !isIntType(*m_enumBaseTy
) &&
2570 !isStringType(*m_enumBaseTy
)) {
2571 raise_error("Invalid base type for enum %s",
2572 m_preClass
->name()->data());
2577 void Class::setNativeDataInfo() {
2578 for (auto cls
= this; cls
; cls
= cls
->parent()) {
2579 if (auto ndi
= cls
->preClass()->nativeDataInfo()) {
2581 m_extra
.raw()->m_nativeDataInfo
= ndi
;
2582 m_extra
.raw()->m_instanceCtor
= Native::nativeDataInstanceCtor
;
2583 m_extra
.raw()->m_instanceDtor
= Native::nativeDataInstanceDtor
;
2589 bool Class::hasNativePropHandler() const {
2590 return m_ODAttrs
& ObjectData::HasNativePropHandler
;
2593 const Native::NativePropHandler
* Class::getNativePropHandler() const {
2594 assert(hasNativePropHandler());
2596 for (auto cls
= this; cls
; cls
= cls
->parent()) {
2597 auto propHandler
= Native::getNativePropHandler(cls
->name());
2598 if (propHandler
!= nullptr) {
2606 void Class::raiseUnsatisfiedRequirement(const PreClass::ClassRequirement
* req
) const {
2607 assert(!(attrs() & (AttrInterface
| AttrTrait
)));
2609 auto const reqName
= req
->name();
2610 if (req
->is_implements()) {
2611 // "require implements" is only allowed on traits.
2613 assert(RuntimeOption::RepoAuthoritative
||
2614 (m_extra
&& m_extra
->m_usedTraits
.size() > 0));
2615 for (auto const& traitCls
: m_extra
->m_usedTraits
) {
2616 if (traitCls
->allRequirements().contains(reqName
)) {
2617 raise_error(Strings::TRAIT_REQ_IMPLEMENTS
,
2618 m_preClass
->name()->data(),
2620 traitCls
->preClass()->name()->data());
2624 if (RuntimeOption::RepoAuthoritative
) {
2625 // As a result of trait flattening, the PreClass of this normal class
2626 // contains a requirement. To save space, we don't include the source
2627 // trait in the requirement. For details, see
2628 // ClassScope::importUsedTraits in the compiler.
2629 assert(!m_extra
|| m_extra
->m_usedTraits
.size() == 0);
2630 assert(m_preClass
->requirements().size() > 0);
2631 raise_error(Strings::TRAIT_REQ_IMPLEMENTS
,
2632 m_preClass
->name()->data(),
2637 always_assert(false); // requirements cannot spontaneously generate
2641 assert(req
->is_extends());
2642 for (auto const& iface
: m_interfaces
.range()) {
2643 if (iface
->allRequirements().contains(reqName
)) {
2644 raise_error("Class '%s' required to extend class '%s'"
2645 " by interface '%s'",
2646 m_preClass
->name()->data(),
2648 iface
->preClass()->name()->data());
2652 for (auto const& traitCls
: m_extra
->m_usedTraits
) {
2653 if (traitCls
->allRequirements().contains(reqName
)) {
2654 raise_error(Strings::TRAIT_REQ_EXTENDS
,
2655 m_preClass
->name()->data(),
2657 traitCls
->preClass()->name()->data());
2661 if (RuntimeOption::RepoAuthoritative
) {
2662 // A result of trait flattening, as with the is_implements case above
2663 assert(!m_extra
|| m_extra
->m_usedTraits
.size() == 0);
2664 assert(m_preClass
->requirements().size() > 0);
2665 raise_error(Strings::TRAIT_REQ_EXTENDS
,
2666 m_preClass
->name()->data(),
2671 // calls to this method are expected to come as a result of an error due
2672 // to a requirement coming from traits or interfaces
2673 always_assert(false);
2676 void Class::checkRequirementConstraints() const {
2677 if (attrs() & (AttrInterface
| AttrTrait
)) return;
2679 for (auto const& req
: m_requirements
.range()) {
2680 auto const reqName
= req
->name();
2681 if (req
->is_implements()) {
2682 if (UNLIKELY(!ifaceofDirect(reqName
))) {
2683 raiseUnsatisfiedRequirement(req
);
2686 auto reqExtCls
= Unit::lookupClass(reqName
);
2688 (reqExtCls
== nullptr) ||
2689 (reqExtCls
->attrs() & (AttrTrait
| AttrInterface
)))) {
2690 // If this class is being created from scratch from the PreClass
2691 // for the first time in this request, then errors would already
2692 // have been raised when the trait/interface from which the
2693 // requirement came was loaded. If however we're subject to the
2694 // whims of Class::avail() and reusing a Class, the failure of the
2695 // lookup indicates that the requirement was not satisfied in the
2696 // previous request; if the requirement had been satisfied, the
2697 // appropriate reqExtCls would again loaded via the class parent
2698 // and interfaces checked in Class::avail()
2699 raiseUnsatisfiedRequirement(req
);
2703 (m_classVecLen
< reqExtCls
->m_classVecLen
) ||
2704 (m_classVec
[reqExtCls
->m_classVecLen
-1] != reqExtCls
))) {
2705 raiseUnsatisfiedRequirement(req
);
2711 void Class::setClassVec() {
2712 if (m_classVecLen
> 1) {
2713 assert(m_parent
.get() != nullptr);
2714 memcpy(m_classVec
, m_parent
->m_classVec
,
2715 (m_classVecLen
-1) * sizeof(m_classVec
[0]));
2717 m_classVec
[m_classVecLen
-1] = this;
2720 void Class::setFuncVec(MethodMapBuilder
& builder
) {
2721 auto funcVec
= this->funcVec();
2723 memset(funcVec
, 0, m_funcVecLen
* sizeof(LowPtr
<Func
>));
2725 funcVec
= (LowPtr
<Func
>*)this;
2726 assert(builder
.size() <= m_funcVecLen
);
2728 for (Slot i
= 0; i
< builder
.size(); i
++) {
2729 assert(builder
[i
]->methodSlot() < builder
.size());
2730 funcVec
[-((int32_t)builder
[i
]->methodSlot() + 1)] = builder
[i
];
2734 void Class::getMethodNames(const Class
* cls
,
2738 // The order of these methods is so that the first ones win on
2739 // case insensitive name conflicts.
2741 auto const numMethods
= cls
->numMethods();
2743 for (Slot i
= 0; i
< numMethods
; ++i
) {
2744 auto const meth
= cls
->getMethod(i
);
2745 auto const declCls
= meth
->cls();
2746 auto addMeth
= [&]() {
2747 auto const methName
= Variant(meth
->name(), Variant::StaticStrInit
{});
2748 auto const lowerName
= f_strtolower(methName
.toString());
2749 if (!out
.exists(lowerName
)) {
2750 out
.add(lowerName
, methName
);
2754 // Only pick methods declared in this class, in order to match
2755 // Zend's order. Inherited methods will be inserted in the
2756 // recursive call later.
2757 if (declCls
!= cls
) continue;
2759 // Skip generated, internal methods.
2760 if (meth
->isGenerated()) continue;
2762 // Public methods are always visible.
2763 if ((meth
->attrs() & AttrPublic
)) {
2768 // In anonymous contexts, only public methods are visible.
2771 // All methods are visible if the context is the class that
2772 // declared them. If the context is not the declCls, protected
2773 // methods are visible in context classes related the declCls.
2774 if (declCls
== ctx
||
2775 ((meth
->attrs() & AttrProtected
) &&
2776 (ctx
->classof(declCls
) || declCls
->classof(ctx
)))) {
2781 // Now add the inherited methods.
2782 if (auto const parent
= cls
->parent()) {
2783 getMethodNames(parent
, ctx
, out
);
2786 // Add interface methods that the class may not have implemented yet.
2787 for (auto& iface
: cls
->declInterfaces()) {
2788 getMethodNames(iface
.get(), ctx
, out
);
2793 ///////////////////////////////////////////////////////////////////////////////
2794 // Trait method import.
2796 bool Class::TMIOps::exclude(const StringData
* methName
) {
2797 return Func::isSpecial(methName
);
2800 void Class::TMIOps::addTraitAlias(Class
* cls
,
2801 Class::TMIOps::alias_type rule
,
2802 const Class
* traitCls
) {
2803 PreClass::TraitAliasRule newRule
{ traitCls
->name(),
2804 rule
.origMethodName(),
2805 rule
.newMethodName(),
2807 cls
->allocExtraData();
2808 cls
->m_extra
.raw()->m_traitAliases
.push_back(newRule
.asNamePair());
2812 Class::TMIOps::findSingleTraitWithMethod(const Class
* cls
,
2813 const StringData
* methName
) {
2814 Class
* traitCls
= nullptr;
2816 for (auto const& t
: cls
->m_extra
->m_usedTraits
) {
2817 // Note: m_methods includes methods from parents/traits recursively.
2818 if (t
->m_methods
.find(methName
)) {
2819 if (traitCls
!= nullptr) {
2820 raise_error("more than one trait contains method '%s'",
2830 Class::TMIOps::findTraitClass(const Class
* cls
,
2831 const StringData
* traitName
) {
2832 return Unit::loadClass(traitName
);
2835 void Class::applyTraitRules(TMIData
& tmid
) {
2836 for (auto const& precRule
: m_preClass
->traitPrecRules()) {
2837 tmid
.applyPrecRule(precRule
);
2839 for (auto const& aliasRule
: m_preClass
->traitAliasRules()) {
2840 tmid
.applyAliasRule(aliasRule
, this);
2844 void Class::importTraitMethod(const TMIData::MethodData
& mdata
,
2845 MethodMapBuilder
& builder
) {
2846 const Func
* method
= mdata
.tm
.method
;
2847 Attr modifiers
= mdata
.tm
.modifiers
;
2849 auto mm_iter
= builder
.find(mdata
.name
);
2851 // For abstract methods, simply return if method already declared.
2852 if ((modifiers
& AttrAbstract
) && mm_iter
!= builder
.end()) {
2856 if (modifiers
== AttrNone
) {
2857 modifiers
= method
->attrs();
2859 // Trait alias statements are only allowed to change the attributes that
2860 // are part 'attrMask' below; all other method attributes are preserved
2861 Attr attrMask
= (Attr
)(AttrPublic
| AttrProtected
| AttrPrivate
|
2862 AttrAbstract
| AttrFinal
);
2863 modifiers
= (Attr
)((modifiers
& (attrMask
)) |
2864 (method
->attrs() & ~(attrMask
)));
2867 Func
* parentMethod
= nullptr;
2868 if (mm_iter
!= builder
.end()) {
2869 Func
* existingMethod
= builder
[mm_iter
->second
];
2870 if (existingMethod
->cls() == this) {
2871 // Don't override an existing method if this class provided an
2875 parentMethod
= existingMethod
;
2877 Func
* f
= method
->clone(this, mdata
.name
);
2879 f
->setAttrs(modifiers
);
2880 if (!parentMethod
) {
2882 builder
.add(mdata
.name
, f
);
2883 f
->setBaseCls(this);
2884 f
->setHasPrivateAncestor(false);
2886 // Override an existing method
2889 methodOverrideCheck(parentMethod
, f
);
2891 assert(!(f
->attrs() & AttrPrivate
) ||
2892 (parentMethod
->attrs() & AttrPrivate
));
2893 if ((parentMethod
->attrs() & AttrPrivate
) || (f
->attrs() & AttrPrivate
)) {
2896 baseClass
= parentMethod
->baseCls();
2898 f
->setBaseCls(baseClass
);
2899 f
->setHasPrivateAncestor(
2900 parentMethod
->hasPrivateAncestor() ||
2901 (parentMethod
->attrs() & AttrPrivate
));
2902 builder
[mm_iter
->second
] = f
;
2906 void Class::importTraitMethods(MethodMapBuilder
& builder
) {
2909 // Find all methods to be imported.
2910 for (auto const& t
: m_extra
->m_usedTraits
) {
2911 Class
* trait
= t
.get();
2912 for (Slot i
= 0; i
< trait
->m_methods
.size(); ++i
) {
2913 Func
* method
= trait
->getMethod(i
);
2914 const StringData
* methName
= method
->name();
2916 TraitMethod traitMethod
{ trait
, method
, method
->attrs() };
2917 tmid
.add(traitMethod
, methName
);
2921 // Apply trait rules and import the methods.
2922 applyTraitRules(tmid
);
2923 auto traitMethods
= tmid
.finish(this);
2925 // Import the methods.
2926 for (auto const& mdata
: traitMethods
) {
2927 importTraitMethod(mdata
, builder
);
2931 ///////////////////////////////////////////////////////////////////////////////