Have Class::{decl,static}Properties() return ranges
[hiphop-php.git] / hphp / runtime / vm / class.cpp
blobb85f79fb99f7ce9259cf65638c43dfe75b4cf08a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
41 #include <algorithm>
42 #include <iostream>
44 TRACE_SET_MOD(class_load);
46 namespace HPHP {
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);
58 Mutex g_classesMutex;
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())) {
78 return acc->second;
81 return f->cls();
85 ///////////////////////////////////////////////////////////////////////////////
86 // Class::PropInitVec.
88 Class::PropInitVec::PropInitVec()
89 : m_data(nullptr)
90 , m_size(0)
91 , m_req_allocated(false)
94 Class::PropInitVec::~PropInitVec() {
95 if (!m_req_allocated) free(m_data);
98 Class::PropInitVec*
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;
105 return p;
108 const Class::PropInitVec&
109 Class::PropInitVec::operator=(const PropInitVec& piv) {
110 assert(!m_req_allocated);
111 if (this != &piv) {
112 unsigned sz = m_size = piv.size();
113 if (sz) sz = folly::nextPowTwo(sz);
114 free(m_data);
115 m_data = (TypedValueAux*)malloc(sz * sizeof(*m_data));
116 assert(m_data);
117 memcpy(m_data, piv.m_data, piv.size() * sizeof(*m_data));
119 return *this;
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));
131 assert(m_data);
133 cellDup(v, m_data[m_size++]);
137 ///////////////////////////////////////////////////////////////////////////////
138 // Class.
140 namespace {
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.
167 continue;
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++;
186 return 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
193 * construct one.
195 constexpr size_t sizeof_Class = Class::classVecOff();
197 template<size_t sz>
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>));
231 try {
232 return new (classPtr) Class(preClass, parent, std::move(usedTraits),
233 classVecLen, funcVecLen);
234 } catch (...) {
235 low_free(mem);
236 throw;
240 Class* Class::rescope(Class* ctx, Attr attrs /* = AttrNone */) {
241 assert(parent() == SystemLib::s_ClosureClass);
242 assert(m_invoke);
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())
295 : template_cls
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
336 * lock.)
338 auto const pcls = m_preClass.get();
339 pcls->namedEntity()->removeClass(this);
340 Treadmill::enqueue(
341 [this] {
342 releaseRefs();
343 if (!this->decAtomicCount()) this->atomicRelease();
348 void Class::atomicRelease() {
349 assert(!m_cachedClass.bound());
350 assert(!getCount());
351 this->~Class();
352 low_free(mallocPtr());
355 Class::~Class() {
356 releaseRefs(); // must be called for Func-nulling side effects
358 if (m_sPropCache) {
359 for (unsigned i = 0, n = numStaticProperties(); i < n; ++i) {
360 m_sPropCache[i].~Link();
362 free(m_sPropCache);
365 for (auto i = size_t{}, n = numMethods(); i < n; i++) {
366 if (auto meth = getMethod(i)) {
367 if (meth->isPreFunc()) {
368 meth->freeClone();
369 } else {
370 Func::destroy(meth);
375 if (m_extra) {
376 delete m_extra.raw();
379 // clean enum cache
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) {
414 m_parent.reset();
417 m_numDeclInterfaces = 0;
418 m_declInterfaces.reset();
419 m_requirements.clear();
421 if (m_extra) {
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()) {
430 if (!parent) {
431 PreClass *ppcls = ourParent->m_preClass.get();
432 parent = Unit::getClass(ppcls->namedEntity(),
433 m_preClass.get()->parent(), tryAutoload);
434 if (!parent) {
435 parent = ourParent;
436 return Avail::Fail;
439 if (parent != ourParent) {
440 if (UNLIKELY(ourParent->isZombie())) {
441 const_cast<Class*>(this)->destroy();
443 return Avail::False;
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,
454 tryAutoload);
455 if (interface != di) {
456 if (interface == nullptr) {
457 parent = di;
458 return Avail::Fail;
460 if (UNLIKELY(di->isZombie())) {
461 const_cast<Class*>(this)->destroy();
463 return Avail::False;
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,
472 tryAutoload);
473 if (trait != usedTrait) {
474 if (trait == nullptr) {
475 parent = usedTrait;
476 return Avail::Fail;
478 if (UNLIKELY(usedTrait->isZombie())) {
479 const_cast<Class*>(this)->destroy();
481 return Avail::False;
484 return Avail::True;
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 {
499 return funcVec();
503 ///////////////////////////////////////////////////////////////////////////////
504 // Ancestry.
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;
511 do {
512 assert(vecIdx < m_classVecLen && vecIdx < cls->m_classVecLen);
513 if (m_classVec[vecIdx] == cls->m_classVec[vecIdx]) {
514 return m_classVec[vecIdx];
516 } while (vecIdx--);
518 return nullptr;
522 ///////////////////////////////////////////////////////////////////////////////
523 // Magic methods.
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()));
532 return m_invoke;
535 ///////////////////////////////////////////////////////////////////////////////
536 // Builtin classes.
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()) {
542 return true;
544 auto info = clsInfo();
545 auto p = this;
546 while ((!info) && (p = p->parent())) {
547 info = p->clsInfo();
549 return info &&
550 (info->getAttribute() & ClassInfo::IsCppSerializable);
553 bool Class::isCollectionClass() const {
554 auto s = name();
555 return collections::isTypeName(s);
559 ///////////////////////////////////////////////////////////////////////////////
560 // Property initialization.
562 void Class::initialize() const {
563 if (m_pinitVec.size() > 0 && getPropData() == nullptr) {
564 initProps();
566 if (numStaticProperties() > 0 && needsInitSProps()) {
567 initSProps();
571 bool Class::initialized() const {
572 if (m_pinitVec.size() > 0 && getPropData() == nullptr) {
573 return false;
575 if (numStaticProperties() > 0 && needsInitSProps()) {
576 return false;
578 return true;
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
588 // the new propVec.
589 auto propVec = PropInitVec::allocWithReqAllocator(m_declPropInit);
591 initPropHandle();
592 *m_propDataCache = propVec;
594 try {
595 // Iteratively invoke 86pinit() methods upward
596 // through the inheritance chain.
597 for (auto it = m_pinitVec.rbegin(); it != m_pinitVec.rend(); ++it) {
598 TypedValue retval;
599 g_context->invokeFunc(&retval, *it, init_null_variant, nullptr,
600 const_cast<Class*>(this));
601 assert(retval.m_type == KindOfNull);
603 } catch (...) {
604 // Undo the allocation of propVec
605 req::destroy_raw_array(propVec->begin(), propVec->size());
606 req::destroy_raw(propVec);
607 *m_propDataCache = nullptr;
608 throw;
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.
617 Slot slot = 0;
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;
623 } else {
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;
645 initSPropHandles();
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++) {
662 TypedValue retval;
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 ///////////////////////////////////////////////////////////////////////////////
674 // Property storage.
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();
684 if (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;
701 } else {
702 propHandle.bind(rds::Mode::Local);
705 auto msg = name()->toCppString() + "::" + sProp.name->toCppString();
706 rds::recordRds(propHandle.handle(),
707 sizeof(TypedValue), "SPropCache", msg);
708 } else {
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
718 * in a dbg build)
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
731 // the wrong time.
732 rds::Link<bool> tmp{rds::kInvalidHandle};
733 tmp.bind(rds::Mode::Persistent);
734 *tmp = true;
735 m_sPropCacheInit = tmp;
736 } else {
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(
757 const Class* ctx,
758 const StringData* key
759 ) const {
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
769 // property
770 auto const baseClass = m_declProperties[propInd].cls;
771 assert(baseClass);
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 };
780 assert(ctx);
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.
785 accessible = false;
786 } else {
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
800 // fail fast here.
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
805 // name.
806 accessible = true;
807 assert(baseClass->classof(ctx));
809 } else {
810 // The property is public (or we're in the debugger and we are bypassing
811 // accessibility checks).
812 accessible = true;
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
818 // name.
820 } else {
821 // We didn't find a visible declared property in this's property map
822 accessible = false;
825 // If ctx is an ancestor of this, check if ctx has a private property with the
826 // same name.
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
834 // have found.
835 return PropLookup<Slot> { ctxPropInd, true };
839 return PropLookup<Slot> { propInd, accessible };
842 Class::PropLookup<Slot> Class::findSProp(
843 const Class* ctx,
844 const StringData* sPropName
845 ) const {
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.
859 case AttrPublic:
860 return true;
862 // Property access is from within a parent class's method, which is
863 // allowed for protected properties.
864 case AttrProtected:
865 return ctx != nullptr && (classof(ctx) || ctx->classof(this));
867 // Can only access private properties via the debugger.
868 case AttrPrivate:
869 return g_context->debuggerSettings.bypassCheck;
871 default: break;
873 not_reached();
874 }();
876 return PropLookup<Slot> { sPropInd, accessible };
879 Class::PropLookup<TypedValue*> Class::getSProp(
880 const Class* ctx,
881 const StringData* sPropName
882 ) const {
883 initialize();
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 ///////////////////////////////////////////////////////////////////////////////
906 // Constants.
908 Cell Class::clsCnsGet(const StringData* clsCnsName, bool includeTypeCns) const {
909 Slot clsCnsInd;
910 auto clsCns = cnsNameToTV(clsCnsName, clsCnsInd, includeTypeCns);
911 if (!clsCns) return make_tv<KindOfUninit>();
912 if (clsCns->m_type != KindOfUninit && !m_constants[clsCnsInd].isType()) {
913 return *clsCns;
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()));
924 } else {
925 auto cCns = clsCnsData->nvGet(clsCnsName);
926 if (cCns) return *cCns;
929 // resolve type constant
930 if (m_constants[clsCnsInd].isType()) {
931 Array resTS;
932 try {
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 */);
941 return tv;
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()))
953 Cell ret;
954 g_context->invokeFuncFew(
955 &ret,
956 meth86cinit,
957 ActRec::encodeClass(this),
958 nullptr,
960 args
963 clsCnsData.set(StrNR(clsCnsName), cellAsCVarRef(ret), true /* isKey */);
964 return ret;
967 const Cell* Class::cnsNameToTV(const StringData* clsCnsName,
968 Slot& clsCnsInd,
969 bool includeTypeCns) const {
970 clsCnsInd = m_constants.findIndex(clsCnsName);
971 if (clsCnsInd == kInvalidSlot) {
972 return nullptr;
974 if (m_constants[clsCnsInd].isAbstract()) {
975 return nullptr;
977 if (!includeTypeCns && m_constants[clsCnsInd].isType()) {
978 return nullptr;
980 auto const ret = const_cast<TypedValueAux*>(&m_constants[clsCnsInd].val);
981 assert(tvIsPlausible(*ret));
982 return ret;
985 DataType Class::clsCnsType(const StringData* cnsName) const {
986 Slot slot;
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;
991 return cns->m_type;
995 ///////////////////////////////////////////////////////////////////////////////
996 // Objects.
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 ///////////////////////////////////////////////////////////////////////////////
1008 // Other methods.
1010 bool Class::verifyPersistent() const {
1011 if (!(attrs() & AttrPersistent)) return false;
1012 if (m_parent.get() &&
1013 !rds::isPersistentHandle(m_parent->classHandle())) {
1014 return false;
1016 for (auto const& declInterface : declInterfaces()) {
1017 if (!rds::isPersistentHandle(declInterface->classHandle())) {
1018 return false;
1021 for (auto const& usedTrait : m_extra->m_usedTraits) {
1022 if (!rds::isPersistentHandle(usedTrait->classHandle())) {
1023 return false;
1026 return true;
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;
1043 bits.set(0);
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())) {
1055 bits.set(bit);
1057 m_instanceBits = bits;
1061 ///////////////////////////////////////////////////////////////////////////////
1062 // Private methods.
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))) {
1090 raise_error(
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()) {
1103 allocExtraData();
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) {
1111 allocExtraData();
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);
1122 f = f->clone(cls);
1123 f->setNewFuncId();
1124 f->setBaseCls(cls);
1125 f->setHasPrivateAncestor(false);
1126 return f;
1129 const StaticString
1130 s_toString("__toString"),
1131 s_construct("__construct"),
1132 s_destruct("__destruct"),
1133 s_invoke("__invoke"),
1134 s_sleep("__sleep"),
1135 s_get("__get"),
1136 s_set("__set"),
1137 s_isset("__isset"),
1138 s_unset("__unset"),
1139 s_call("__call"),
1140 s_callStatic("__callStatic"),
1141 s_clone("__clone");
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()) {
1161 m_invoke = nullptr;
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)) {
1168 m_ctor = func;
1169 return true;
1171 return false;
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)) {
1179 throw Exception(
1180 "%s has colliding constructor definitions coming from traits",
1181 m_preClass->name()->data()
1184 return;
1187 if (!(attrs() & AttrTrait)) {
1188 // Look for Foo::Foo() declared in this class
1189 if (matchedClassOrIsTrait(m_preClass->name())) {
1190 return;
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;
1198 return;
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));
1209 namespace {
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 "
1215 "that of %s::%s()",
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();
1227 } else {
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
1246 && func->isNative()
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;
1267 size_t i = 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;
1294 if (ivariadic) {
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);
1331 } // namespace
1333 Class::Class(PreClass* preClass, Class* parent,
1334 std::vector<ClassPtr>&& usedTraits,
1335 unsigned classVecLen, unsigned funcVecLen)
1336 : m_parent(parent)
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()) {
1342 allocExtraData();
1343 m_extra.raw()->m_usedTraits = std::move(usedTraits);
1345 setParent();
1346 setMethods();
1347 setSpecial(); // must run before setODAttributes
1348 setODAttributes();
1349 setInterfaces();
1350 setConstants();
1351 setProperties();
1352 setInitializers();
1353 setClassVec();
1354 setRequirements();
1355 setNativeDataInfo();
1356 setEnumType();
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 "
1380 "class %s",
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))) {
1388 raise_error(
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);
1419 assert(f);
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
1426 // overriden below.
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
1436 // parent.
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.
1447 continue;
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);
1457 // Overlay.
1458 Func* f = method->clone(this);
1459 f->setNewFuncId();
1460 Class* baseClass;
1461 assert(!(f->attrs() & AttrPrivate) ||
1462 (parentMethod->attrs() & AttrPrivate));
1463 if ((parentMethod->attrs() & AttrPrivate) || (f->attrs() & AttrPrivate)) {
1464 baseClass = this;
1465 } else {
1466 baseClass = parentMethod->baseCls();
1468 f->setBaseCls(baseClass);
1469 f->setHasPrivateAncestor(
1470 parentMethod->hasPrivateAncestor() ||
1471 (parentMethod->attrs() & AttrPrivate));
1472 builder[it2->second] = f;
1473 } else {
1474 // This is the first class that declares the method
1475 Class* baseClass = this;
1476 // Append.
1477 Func* f = method->clone(this);
1478 f->setNewFuncId();
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
1492 // static locals
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
1501 // class.
1502 f = f->clone(f->attrs() & AttrClone ? this : f->cls());
1503 f->setNewFuncId();
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
1515 // process unique
1516 assert(false);
1522 if (Class::MethodCreateHook) {
1523 Class::MethodCreateHook(this, builder);
1524 // running MethodCreateHook may add methods to builder
1525 traitsEndIdx = builder.size();
1528 if (m_extra) {
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)) {
1552 raise_error(
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() {
1568 m_ODAttrs = 0;
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);
1609 continue;
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()) {
1625 continue;
1628 if (existingConst.isAbstract()) {
1629 existingConst.cls = iConst.cls;
1630 existingConst.val = iConst.val;
1631 continue;
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
1654 // overridden.
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 "
1660 "%s::%s in %s",
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();
1690 } else {
1691 // Append constant.
1692 Const constant;
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
1701 // defined
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
1717 else if (
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()) {
1722 raise_error(
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);
1739 } else {
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.
1763 Prop prop;
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);
1782 } else {
1783 ++numInaccessible;
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.
1792 SProp sProp;
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.
1841 if (parentProp
1842 && (preProp->attrs() & (AttrPublic|AttrProtected|AttrPrivate))
1843 > (parentProp->attrs & (AttrPublic|AttrProtected|AttrPrivate))) {
1844 raise_error(
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)) {
1854 case AttrPrivate: {
1855 // Append a new private property.
1856 Prop prop;
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
1862 prop.cls = this;
1863 prop.typeConstraint = preProp->typeConstraint();
1864 prop.docComment = preProp->docComment();
1865 prop.repoAuthType = preProp->repoAuthType();
1866 if (slot < traitIdx) {
1867 prop.idx = slot;
1868 } else {
1869 prop.idx = slot + m_preClass->numProperties() + traitOffset;
1871 curPropMap.add(preProp->name(), prop);
1872 m_declPropInit.push_back(m_preClass->lookupProp(preProp->name())
1873 ->val());
1874 break;
1876 case AttrProtected: {
1877 // Check whether a superclass has already declared this protected
1878 // property.
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)) ==
1883 AttrProtected);
1884 prop.cls = this;
1885 prop.docComment = preProp->docComment();
1886 if (slot < traitIdx) {
1887 prop.idx = slot;
1888 } else {
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);
1896 break;
1898 // Append a new protected property.
1899 Prop prop;
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
1906 prop.cls = this;
1907 prop.docComment = preProp->docComment();
1908 prop.repoAuthType = preProp->repoAuthType();
1909 if (slot < traitIdx) {
1910 prop.idx = slot;
1911 } else {
1912 prop.idx = slot + m_preClass->numProperties() + traitOffset;
1914 curPropMap.add(preProp->name(), prop);
1915 m_declPropInit.push_back(m_preClass->lookupProp(preProp->name())
1916 ->val());
1917 break;
1919 case AttrPublic: {
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];
1925 prop.cls = this;
1926 prop.docComment = preProp->docComment();
1927 if ((prop.attrs & (AttrPublic|AttrProtected|AttrPrivate))
1928 == AttrProtected) {
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) {
1936 prop.idx = slot;
1937 } else {
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);
1945 break;
1947 // Append a new public property.
1948 Prop prop;
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
1955 prop.cls = this;
1956 prop.docComment = preProp->docComment();
1957 prop.repoAuthType = preProp->repoAuthType();
1958 if (slot < traitIdx) {
1959 prop.idx = slot;
1960 } else {
1961 prop.idx = slot + m_preClass->numProperties() + traitOffset;
1963 curPropMap.add(preProp->name(), prop);
1964 m_declPropInit.push_back(m_preClass->lookupProp(preProp->name())
1965 ->val());
1966 break;
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))) {
1989 raise_error(
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) {
1999 SProp sProp;
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();
2009 sProp.cls = this;
2010 sProp.val = m_preClass->lookupProp(preProp->name())->val();
2011 sProp.repoAuthType = preProp->repoAuthType();
2012 if (slot < traitIdx) {
2013 sProp.idx = slot;
2014 } else {
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];
2027 if (prop.idx < 0) {
2028 prop.idx = idxOffset - prop.idx;
2029 if (curIdx < prop.idx) {
2030 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) {
2039 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) {
2062 case KindOfNull:
2063 return true;
2065 case KindOfBoolean:
2066 case KindOfInt64:
2067 case KindOfDouble:
2068 case KindOfStaticString:
2069 case KindOfString:
2070 return same(tvAsVariant(&tv1), tvAsVariant(&tv2));
2072 case KindOfUninit:
2073 case KindOfArray:
2074 case KindOfObject:
2075 case KindOfResource:
2076 case KindOfRef:
2077 return false;
2079 case KindOfClass:
2080 break;
2082 not_reached();
2085 void Class::importTraitInstanceProp(Class* trait,
2086 Prop& traitProp,
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(),
2099 prop.name,
2100 prop.attrs);
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);
2108 } else {
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,
2121 SProp& traitProp,
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);
2138 } else {
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;
2146 } else {
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,
2179 curPropMap);
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,
2186 curSPropMap);
2189 idxOffset += trait->m_declProperties.size() +
2190 trait->m_staticProperties.size();
2194 void Class::addTraitPropInitializers(std::vector<const Func*>& thisInitVec,
2195 bool staticProps) {
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);
2206 f->setNewFuncId();
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
2221 // initialization.
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());
2235 if (sinit) {
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.
2282 continue;
2284 } else {
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(),
2290 methName->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 "
2297 "in class %s",
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);
2379 if (m_toString) {
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;
2403 Slot maxSlot = 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));
2416 auto cursor = mem;
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()));
2448 vtable[i] = func;
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);
2479 if (!reqCls) {
2480 raise_error("%s '%s' required by trait '%s' cannot be loaded",
2481 req.is_extends() ? "Class" : "Interface",
2482 reqName->data(),
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(),
2490 reqName->data(),
2491 reqName->data());
2493 } else {
2494 assert(req.is_implements());
2495 if (!(reqCls->attrs() & AttrInterface)) {
2496 raise_error(Strings::TRAIT_BAD_REQ_IMPLEMENTS,
2497 m_preClass->name()->data(),
2498 reqName->data(),
2499 reqName->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);
2510 if (!reqCls) {
2511 raise_error("'%s' required by interface '%s' cannot be loaded",
2512 reqName->data(),
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(),
2521 reqName->data(),
2522 reqName->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);
2532 if (!reqCls) {
2533 raise_error("%s '%s' required by trait '%s' cannot be loaded",
2534 req.is_extends() ? "Class" : "Interface",
2535 reqName->data(),
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(),
2543 reqName->data(),
2544 reqName->data());
2546 } else {
2547 assert(req.is_implements());
2548 if (!(reqCls->attrs() & AttrInterface)) {
2549 raise_error(Strings::TRAIT_BAD_REQ_IMPLEMENTS,
2550 m_preClass->name()->data(),
2551 reqName->data(),
2552 reqName->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.
2568 if (m_enumBaseTy &&
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()) {
2580 allocExtraData();
2581 m_extra.raw()->m_nativeDataInfo = ndi;
2582 m_extra.raw()->m_instanceCtor = Native::nativeDataInstanceCtor;
2583 m_extra.raw()->m_instanceDtor = Native::nativeDataInstanceDtor;
2584 break;
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) {
2599 return propHandler;
2603 not_reached();
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(),
2619 reqName->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(),
2633 reqName->data(),
2634 "<<flattened>>");
2637 always_assert(false); // requirements cannot spontaneously generate
2638 return;
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(),
2647 reqName->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(),
2656 reqName->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(),
2667 reqName->data(),
2668 "<<flattened>>");
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);
2685 } else {
2686 auto reqExtCls = Unit::lookupClass(reqName);
2687 if (UNLIKELY(
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);
2702 if (UNLIKELY(
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,
2735 const Class* ctx,
2736 Array& out) {
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)) {
2764 addMeth();
2765 continue;
2768 // In anonymous contexts, only public methods are visible.
2769 if (!ctx) continue;
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)))) {
2777 addMeth();
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(),
2806 rule.modifiers() };
2807 cls->allocExtraData();
2808 cls->m_extra.raw()->m_traitAliases.push_back(newRule.asNamePair());
2811 const Class*
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'",
2821 methName->data());
2823 traitCls = t.get();
2826 return traitCls;
2829 const Class*
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()) {
2853 return;
2856 if (modifiers == AttrNone) {
2857 modifiers = method->attrs();
2858 } else {
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
2872 // implementation
2873 return;
2875 parentMethod = existingMethod;
2877 Func* f = method->clone(this, mdata.name);
2878 f->setNewFuncId();
2879 f->setAttrs(modifiers);
2880 if (!parentMethod) {
2881 // New method
2882 builder.add(mdata.name, f);
2883 f->setBaseCls(this);
2884 f->setHasPrivateAncestor(false);
2885 } else {
2886 // Override an existing method
2887 Class* baseClass;
2889 methodOverrideCheck(parentMethod, f);
2891 assert(!(f->attrs() & AttrPrivate) ||
2892 (parentMethod->attrs() & AttrPrivate));
2893 if ((parentMethod->attrs() & AttrPrivate) || (f->attrs() & AttrPrivate)) {
2894 baseClass = this;
2895 } else {
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) {
2907 TMIData tmid;
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 ///////////////////////////////////////////////////////////////////////////////