2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/complex_types.h"
17 #include "hphp/runtime/base/comparisons.h"
18 #include "hphp/runtime/base/array/hphp_array.h"
19 #include "hphp/util/util.h"
20 #include "hphp/util/debug.h"
21 #include "hphp/runtime/vm/jit/targetcache.h"
22 #include "hphp/runtime/vm/jit/translator.h"
23 #include "hphp/runtime/vm/treadmill.h"
24 #include "hphp/runtime/vm/name_value_table.h"
25 #include "hphp/runtime/vm/name_value_table_wrapper.h"
26 #include "hphp/runtime/vm/request_arena.h"
27 #include "hphp/system/systemlib.h"
28 #include "hphp/util/logger.h"
29 #include "hphp/util/parser/parser.h"
31 #include <boost/optional.hpp>
32 #include <boost/utility/typed_in_place_factory.hpp>
39 static StringData
* sd86ctor
= StringData::GetStaticString("86ctor");
40 static StringData
* sd86pinit
= StringData::GetStaticString("86pinit");
41 static StringData
* sd86sinit
= StringData::GetStaticString("86sinit");
43 hphp_hash_map
<const StringData
*, const HhbcExtClassInfo
*,
44 string_data_hash
, string_data_isame
> Class::s_extClassHash
;
45 Class::InstanceCounts
Class::s_instanceCounts
;
46 ReadWriteMutex
Class::s_instanceCountsLock(RankInstanceCounts
);
47 Class::InstanceBitsMap
Class::s_instanceBits
;
48 ReadWriteMutex
Class::s_instanceBitsLock(RankInstanceBits
);
49 std::atomic
<bool> Class::s_instanceBitsInit
{false};
51 const StringData
* PreClass::manglePropName(const StringData
* className
,
52 const StringData
* propName
,
54 switch (attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
)) {
59 std::string mangledName
= "";
60 mangledName
.push_back('\0');
61 mangledName
.push_back('*');
62 mangledName
.push_back('\0');
63 mangledName
+= propName
->data();
64 return StringData::GetStaticString(mangledName
);
67 std::string mangledName
= "";
68 mangledName
.push_back('\0');
69 mangledName
+= className
->data();
70 mangledName
.push_back('\0');
71 mangledName
+= propName
->data();
72 return StringData::GetStaticString(mangledName
);
74 default: not_reached();
78 //=============================================================================
81 PreClass::Prop::Prop(PreClass
* preClass
,
84 const StringData
* typeConstraint
,
85 const StringData
* docComment
,
86 const TypedValue
& val
,
88 : m_preClass(preClass
)
91 , m_typeConstraint(typeConstraint
)
92 , m_docComment(docComment
)
93 , m_hphpcType(hphpcType
)
95 m_mangledName
= manglePropName(preClass
->name(), n
, attrs
);
96 memcpy(&m_val
, &val
, sizeof(TypedValue
));
99 void PreClass::Prop::prettyPrint(std::ostream
& out
) const {
101 if (m_attrs
& AttrStatic
) { out
<< "static "; }
102 if (m_attrs
& AttrPublic
) { out
<< "public "; }
103 if (m_attrs
& AttrProtected
) { out
<< "protected "; }
104 if (m_attrs
& AttrPrivate
) { out
<< "private "; }
105 out
<< m_preClass
->name()->data() << "::" << m_name
->data() << " = ";
106 if (m_val
.m_type
== KindOfUninit
) {
107 out
<< "<non-scalar>";
109 std::stringstream ss
;
110 staticStreamer(&m_val
, ss
);
116 //=============================================================================
119 PreClass::Const::Const(PreClass
* preClass
, const StringData
* n
,
120 const StringData
* typeConstraint
,
121 const TypedValue
& val
, const StringData
* phpCode
)
122 : m_preClass(preClass
), m_name(n
), m_typeConstraint(typeConstraint
),
124 memcpy(&m_val
, &val
, sizeof(TypedValue
));
127 void PreClass::Const::prettyPrint(std::ostream
& out
) const {
128 out
<< "Constant " << m_preClass
->name()->data() << "::" << m_name
->data()
130 if (m_val
.m_type
== KindOfUninit
) {
131 out
<< "<non-scalar>";
133 std::stringstream ss
;
134 staticStreamer(&m_val
, ss
);
140 //=============================================================================
143 PreClass::PreClass(Unit
* unit
, int line1
, int line2
, Offset o
,
144 const StringData
* n
, Attr attrs
, const StringData
* parent
,
145 const StringData
* docComment
, Id id
, Hoistable hoistable
)
146 : m_unit(unit
), m_line1(line1
), m_line2(line2
), m_offset(o
), m_id(id
),
147 m_builtinPropSize(0), m_attrs(attrs
), m_hoistable(hoistable
),
148 m_name(n
), m_parent(parent
), m_docComment(docComment
),
149 m_InstanceCtor(nullptr) {
150 m_namedEntity
= Unit::GetNamedEntity(n
);
153 PreClass::~PreClass() {
154 std::for_each(methods(), methods() + numMethods(), Func::destroy
);
157 void PreClass::atomicRelease() {
161 void PreClass::prettyPrint(std::ostream
&out
) const {
163 if (m_attrs
& AttrAbstract
) { out
<< "abstract "; }
164 if (m_attrs
& AttrFinal
) { out
<< "final "; }
165 if (m_attrs
& AttrInterface
) { out
<< "interface "; }
166 out
<< m_name
->data() << " at " << m_offset
;
167 if (m_hoistable
== MaybeHoistable
) {
168 out
<< " (maybe-hoistable)";
169 } else if (m_hoistable
== AlwaysHoistable
) {
170 out
<< " (always-hoistable)";
173 out
<< " (ID " << m_id
<< ")";
177 for (Func
* const* it
= methods(); it
!= methods() + numMethods(); ++it
) {
179 (*it
)->prettyPrint(out
);
181 for (const Prop
* it
= properties();
182 it
!= properties() + numProperties();
185 it
->prettyPrint(out
);
187 for (const Const
* it
= constants();
188 it
!= constants() + numConstants();
191 it
->prettyPrint(out
);
195 //=============================================================================
198 Class
* Class::newClass(PreClass
* preClass
, Class
* parent
) {
199 unsigned classVecLen
= (parent
!= nullptr) ? parent
->m_classVecLen
+1 : 1;
200 void* mem
= Util::low_malloc(sizeForNClasses(classVecLen
));
202 return new (mem
) Class(preClass
, parent
, classVecLen
);
209 Class::Class(PreClass
* preClass
, Class
* parent
, unsigned classVecLen
)
210 : m_preClass(PreClassPtr(preClass
)), m_parent(parent
),
211 m_traitsBeginIdx(0), m_traitsEndIdx(0), m_clsInfo(nullptr),
212 m_builtinPropSize(0), m_classVecLen(classVecLen
), m_cachedOffset(0),
213 m_propDataCache(-1), m_propSDataCache(-1), m_InstanceCtor(nullptr),
214 m_nextClass(nullptr) {
230 auto methods
= methodRange();
231 while (!methods
.empty()) {
232 Func
* meth
= methods
.popFront();
233 if (meth
) Func::destroy(meth
);
237 void Class::releaseRefs() {
239 * We have to be careful here.
240 * We want to free up as much as possible as early as possible, but
241 * some of our methods may actually belong to our parent
242 * This means we can't destroy *our* Funcs until our refCount
243 * hits zero (ie when Class::~Class gets called), because there
244 * could be a child class which hasn't yet been destroyed, which
245 * will need to inspect them. Also, we need to inspect the Funcs
246 * now (while we still have a references to parent) to determine
247 * which ones we will eventually need to free.
248 * Similarly, if any of our func's belong to a parent class, we
249 * can't free the parent, because one of our children could also
250 * have a reference to those func's (and its only reference to
251 * our parent is via this class).
253 auto methods
= mutableMethodRange();
254 bool okToReleaseParent
= true;
255 while (!methods
.empty()) {
256 Func
*& meth
= methods
.popFront();
257 if (meth
/* releaseRefs can be called more than once */ &&
258 meth
->cls() != this &&
259 ((meth
->attrs() & AttrPrivate
) || !meth
->hasStaticLocals())) {
261 okToReleaseParent
= false;
265 if (okToReleaseParent
) {
268 m_declInterfaces
.clear();
269 m_usedTraits
.clear();
274 class FreeClassTrigger
: public Treadmill::WorkItem
{
275 TRACE_SET_MOD(treadmill
);
278 explicit FreeClassTrigger(Class
* cls
) : m_cls(cls
) {
279 TRACE(3, "FreeClassTrigger @ %p, cls %p\n", this, m_cls
);
282 TRACE(3, "FreeClassTrigger: Firing @ %p , cls %p\n", this, m_cls
);
283 if (!m_cls
->decAtomicCount()) {
284 m_cls
->atomicRelease();
291 void Class::destroy() {
293 * If we were never put on NamedEntity::classList, or
294 * we've already been destroy'd, there's nothing to do
296 if (!m_cachedOffset
) return;
298 Lock
l(Unit::s_classesMutex
);
299 // Need to recheck now we have the lock
300 if (!m_cachedOffset
) return;
301 // Only do this once.
304 PreClass
* pcls
= m_preClass
.get();
305 pcls
->namedEntity()->removeClass(this);
307 * Regardless of refCount, this Class is now unusable.
308 * Release what we can immediately, to allow dependent
309 * classes to be freed.
310 * Needs to be under the lock, because multiple threads
314 Treadmill::WorkItem::enqueue(new FreeClassTrigger(this));
317 void Class::atomicRelease() {
318 assert(!m_cachedOffset
);
321 Util::low_free(this);
324 Class
*Class::getCached() const {
325 return *(Class
**)Transl::TargetCache::handleToPtr(m_cachedOffset
);
328 void Class::setCached() {
329 *(Class
**)Transl::TargetCache::handleToPtr(m_cachedOffset
) = this;
332 bool Class::verifyPersistent() const {
333 if (!(attrs() & AttrPersistent
)) return false;
334 if (m_parent
.get() &&
335 !Transl::TargetCache::isPersistentHandle(m_parent
->m_cachedOffset
)) {
338 for (auto const& declInterface
: m_declInterfaces
) {
339 if (!Transl::TargetCache::isPersistentHandle(
340 declInterface
->m_cachedOffset
)) {
344 for (auto const& usedTrait
: m_usedTraits
) {
345 if (!Transl::TargetCache::isPersistentHandle(
346 usedTrait
->m_cachedOffset
)) {
353 const Func
* Class::getDeclaredCtor() const {
354 const Func
* f
= getCtor();
355 return f
->name() != sd86ctor
? f
: nullptr;
358 void Class::initInstanceBits() {
359 assert(Transl::Translator::WriteLease().amOwner());
360 if (s_instanceBitsInit
.load(std::memory_order_acquire
)) return;
362 // First, grab a write lock on s_instanceCounts and grab the current set of
363 // counts as quickly as possible to minimize blocking other threads still
364 // trying to profile instance checks.
365 typedef std::pair
<const StringData
*, unsigned> Count
;
366 std::vector
<Count
> counts
;
369 // If you think of the read-write lock as a shared-exclusive lock instead,
370 // the fact that we're grabbing a write lock to iterate over the table
371 // makes more sense: it's safe to concurrently modify a
372 // tbb::concurrent_hash_map, but iteration is not guaranteed to be safe
373 // with concurrent insertions.
374 WriteLock
l(s_instanceCountsLock
);
375 for (auto& pair
: s_instanceCounts
) {
376 counts
.push_back(pair
);
377 total
+= pair
.second
;
380 std::sort(counts
.begin(), counts
.end(), [&](const Count
& a
, const Count
& b
) {
381 return a
.second
> b
.second
;
384 // Next, initialize s_instanceBits with the top 127 most checked classes. Bit
385 // 0 is reserved as an 'initialized' flag
388 for (auto& item
: counts
) {
389 if (i
>= kInstanceBits
) break;
390 if (Class
* cls
= Unit::lookupUniqueClass(item
.first
)) {
391 if (!(cls
->attrs() & AttrUnique
)) {
395 s_instanceBits
[item
.first
] = i
;
396 accum
+= item
.second
;
400 // Print out stats about what we ended up using
401 if (Trace::moduleEnabledRelease(Trace::instancebits
, 1)) {
402 Trace::traceRelease("%s: %u classes, %" PRIu64
" (%.2f%%) of warmup"
404 __FUNCTION__
, i
-1, accum
, 100.0 * accum
/ total
);
405 if (Trace::moduleEnabledRelease(Trace::instancebits
, 2)) {
408 for (auto& pair
: counts
) {
410 Trace::traceRelease("skipping the remainder of the %zu classes\n",
414 accum
+= pair
.second
;
415 Trace::traceRelease("%3u %5.2f%% %7u -- %6.2f%% %7" PRIu64
" %s\n",
416 i
++, 100.0 * pair
.second
/ total
, pair
.second
,
417 100.0 * accum
/ total
, accum
,
423 // Finally, update m_instanceBits on every Class that currently exists. This
424 // must be done while holding a lock that blocks insertion of new Classes
425 // into their class lists, but in practice most Classes will already be
426 // created by now and this process takes at most 10ms.
427 WriteLock
l(s_instanceBitsLock
);
428 for (AllClasses ac
; !ac
.empty(); ) {
429 Class
* c
= ac
.popFront();
430 c
->setInstanceBitsAndParents();
433 s_instanceBitsInit
.store(true, std::memory_order_release
);
436 void Class::profileInstanceOf(const StringData
* name
) {
437 assert(name
->isStatic());
439 Class
* c
= Unit::lookupClass(name
);
440 if (c
&& (c
->attrs() & AttrInterface
)) {
444 InstanceCounts::accessor acc
;
446 // The extra layer of locking is here so that initInstanceBits can safely
447 // iterate over s_instanceCounts while building its map of names to bits.
448 ReadLock
l(s_instanceCountsLock
);
449 if (!s_instanceCounts
.insert(acc
, InstanceCounts::value_type(name
, inc
))) {
454 bool Class::haveInstanceBit(const StringData
* name
) {
455 assert(Transl::Translator::WriteLease().amOwner());
456 assert(s_instanceBitsInit
.load(std::memory_order_acquire
));
457 return mapContains(s_instanceBits
, name
);
460 bool Class::getInstanceBitMask(const StringData
* name
,
461 int& offset
, uint8_t& mask
) {
462 assert(Transl::Translator::WriteLease().amOwner());
463 assert(s_instanceBitsInit
.load(std::memory_order_acquire
));
464 const size_t bitWidth
= sizeof(mask
) * CHAR_BIT
;
466 if (!mapGet(s_instanceBits
, name
, &bit
)) return false;
467 assert(bit
>= 1 && bit
< kInstanceBits
);
468 offset
= offsetof(Class
, m_instanceBits
) + bit
/ bitWidth
* sizeof(mask
);
469 mask
= 1u << (bit
% bitWidth
);
474 * Check whether a Class from a previous request is available to be defined.
475 * The caller should check that it has the same preClass that is being defined.
476 * Being available means that the parent, the interfaces and the traits are
477 * already defined (or become defined via autoload, if tryAutoload is true).
479 * returns Avail::True - if it is available
480 * Avail::Fail - if it is impossible to define the class at this point
481 * Avail::False- if this particular Class* cant be defined at this point
483 * Note that Fail means that at least one of the parent, interfaces and traits
484 * was not defined at all, while False means that at least one was defined but
485 * did not correspond to this Class*
487 * The parent parameter is used for two purposes: first it avoids looking up the
488 * active parent class for each potential Class*; and second its used on
489 * Fail to return the problem class so the caller can report the error
492 Class::Avail
Class::avail(Class
*& parent
, bool tryAutoload
/*=false*/) const {
493 if (Class
*ourParent
= m_parent
.get()) {
495 PreClass
*ppcls
= ourParent
->m_preClass
.get();
496 parent
= Unit::getClass(ppcls
->namedEntity(), ppcls
->name(), tryAutoload
);
502 if (parent
!= ourParent
) {
503 if (UNLIKELY(ourParent
->isZombie())) {
504 const_cast<Class
*>(this)->destroy();
509 for (auto const& di
: m_declInterfaces
) {
510 Class
* declInterface
= di
.get();
511 PreClass
*pint
= declInterface
->m_preClass
.get();
512 Class
* interface
= Unit::getClass(pint
->namedEntity(), pint
->name(),
514 if (interface
!= declInterface
) {
515 if (interface
== nullptr) {
516 parent
= declInterface
;
519 if (UNLIKELY(declInterface
->isZombie())) {
520 const_cast<Class
*>(this)->destroy();
525 for (auto const& ut
: m_usedTraits
) {
526 Class
* usedTrait
= ut
.get();
527 PreClass
* ptrait
= usedTrait
->m_preClass
.get();
528 Class
* trait
= Unit::getClass(ptrait
->namedEntity(), ptrait
->name(),
530 if (trait
!= usedTrait
) {
531 if (trait
== nullptr) {
535 if (UNLIKELY(usedTrait
->isZombie())) {
536 const_cast<Class
*>(this)->destroy();
544 void Class::initialize(TypedValue
*& sProps
) const {
545 if (m_pinitVec
.size() > 0) {
546 if (getPropData() == nullptr) {
550 // The asymmetry between the logic around initProps() above and initSProps()
551 // below is due to the fact that instance properties only require storage in
552 // g_vmContext if there are non-scalar initializers involved, whereas static
553 // properties *always* require storage in g_vmContext.
554 if (numStaticProperties() > 0) {
555 if ((sProps
= getSPropData()) == nullptr) {
556 sProps
= initSProps();
563 void Class::initialize() const {
568 Class::PropInitVec
* Class::initPropsImpl() const {
569 assert(m_pinitVec
.size() > 0);
570 assert(getPropData() == nullptr);
571 // Copy initial values for properties to a new vector that can be used to
572 // complete initialization for non-scalar properties via the iterative
573 // 86pinit() calls below. 86pinit() takes a reference to an array to populate
574 // with initial property values; after it completes, we copy the values into
576 request_arena().beginFrame();
577 PropInitVec
* propVec
= PropInitVec::allocInRequestArena(m_declPropInit
);
578 size_t nProps
= numDeclProperties();
583 HphpArray
* propArr
= ArrayData::Make(nProps
);
584 Variant
arg0(propArr
);
586 args
.appendRef(arg0
);
587 assert(propArr
->getCount() == 1); // Don't want to trigger COW
589 // Create a sentinel that uniquely identifies uninitialized properties.
590 ObjectData
* sentinel
= SystemLib::AllocPinitSentinel();
591 sentinel
->incRefCount();
593 tv
.m_data
.pobj
= sentinel
;
594 tv
.m_type
= KindOfObject
;
595 args
.append(tvAsCVarRef(&tv
));
596 sentinel
->decRefCount();
599 TypedValue
* tvSentinel
= args
->nvGetValueRef(1);
600 for (size_t i
= 0; i
< nProps
; ++i
) {
601 TypedValue
& prop
= (*propVec
)[i
];
602 // We have to use m_originalMangledName here because the
603 // 86pinit methods for traits depend on it
604 auto const* k
= (m_declProperties
[i
].m_attrs
& AttrPrivate
)
605 ? m_declProperties
[i
].m_originalMangledName
606 : m_declProperties
[i
].m_name
;
608 // Replace undefined values with tvSentinel, which acts as a
609 // unique sentinel for undefined properties in 86pinit().
610 if (prop
.m_type
== KindOfUninit
) {
611 propArr
->nvInsert(const_cast<StringData
*>(k
), tvSentinel
);
613 // This may seem pointless, but if you don't populate all the keys,
614 // you'll get "undefined index" notices in the case where a
615 // scalar-initialized property overrides a parent's
616 // non-scalar-initialized property of the same name.
617 propArr
->nvInsert(const_cast<StringData
*>(k
), &prop
);
622 // Iteratively invoke 86pinit() methods upward
623 // through the inheritance chain.
624 for (Class::InitVec::const_reverse_iterator it
= m_pinitVec
.rbegin();
625 it
!= m_pinitVec
.rend(); ++it
) {
627 g_vmContext
->invokeFunc(&retval
, *it
, args
, nullptr,
628 const_cast<Class
*>(this));
629 assert(retval
.m_type
== KindOfNull
);
632 // Undo the allocation of propVec
633 request_arena().endFrame();
637 // Pull the values out of the populated array and put them in propVec
638 for (size_t i
= 0; i
< nProps
; ++i
) {
639 TypedValue
& prop
= (*propVec
)[i
];
640 if (prop
.m_type
== KindOfUninit
) {
641 auto const* k
= (m_declProperties
[i
].m_attrs
& AttrPrivate
)
642 ? m_declProperties
[i
].m_originalMangledName
643 : m_declProperties
[i
].m_name
;
645 auto const* value
= propArr
->nvGet(k
);
652 // For properties that do not require deep initialization, promote strings
653 // and arrays that came from 86pinit to static. This allows us to initialize
654 // object properties very quickly because we can just memcpy and we don't
655 // have to do any refcounting.
656 // For properties that require "deep" initialization, we have to do a little
657 // more work at object creation time.
659 for (PropInitVec::iterator it
= propVec
->begin();
660 it
!= propVec
->end(); ++it
, ++slot
) {
661 TypedValueAux
* tv
= &(*it
);
662 // Set deepInit if the property requires "deep" initialization.
663 if (m_declProperties
[slot
].m_attrs
& AttrDeepInit
) {
664 tv
->deepInit() = true;
666 tvAsVariant(tv
).setEvalScalar();
667 tv
->deepInit() = false;
674 Slot
Class::getDeclPropIndex(Class
* ctx
, const StringData
* key
,
675 bool& accessible
) const {
676 Slot propInd
= lookupDeclProp(key
);
677 if (propInd
!= kInvalidSlot
) {
678 Attr attrs
= m_declProperties
[propInd
].m_attrs
;
679 if ((attrs
& (AttrProtected
|AttrPrivate
)) &&
680 !g_vmContext
->getDebuggerBypassCheck()) {
681 // Fetch 'baseClass', which is the class in the inheritance
682 // tree which first declared the property
683 Class
* baseClass
= m_declProperties
[propInd
].m_class
;
685 // If ctx == baseClass, we know we have the right property
686 // and we can stop here.
687 if (ctx
== baseClass
) {
691 // The anonymous context cannot access protected or private
692 // properties, so we can fail fast here.
693 if (ctx
== nullptr) {
698 if (attrs
& AttrPrivate
) {
699 // ctx != baseClass and the property is private, so it is not
700 // accessible. We need to keep going because ctx may define a
701 // private property with this name.
704 if (ctx
->classof(baseClass
)) {
705 // ctx is derived from baseClass, so we know this protected
706 // property is accessible and we know ctx cannot have private
707 // property with the same name, so we're done.
711 if (!baseClass
->classof(ctx
)) {
712 // ctx is not the same, an ancestor, or a descendent of baseClass,
713 // so the property is not accessible. Also, we know that ctx cannot
714 // be the same or an ancestor of this, so we don't need to check if
715 // ctx declares a private property with the same name and we can
720 // We now know this protected property is accessible, but we need to
721 // keep going because ctx may define a private property with the same
724 assert(baseClass
->classof(ctx
));
727 // The property is public (or we're in the debugger and we are bypassing
728 // accessibility checks).
730 // If ctx == this, we don't have to check if ctx defines a private
731 // property with the same name and we can stop here.
735 // We still need to check if ctx defines a private property with the
739 // We didn't find a visible declared property in this's property map
742 // If ctx is an ancestor of this, check if ctx has a private property
743 // with the same name.
744 if (ctx
&& classof(ctx
)) {
745 Slot ctxPropInd
= ctx
->lookupDeclProp(key
);
746 if (ctxPropInd
!= kInvalidSlot
&&
747 ctx
->m_declProperties
[ctxPropInd
].m_class
== ctx
&&
748 (ctx
->m_declProperties
[ctxPropInd
].m_attrs
& AttrPrivate
)) {
749 // A private property from ctx trumps any other property we may
758 TypedValue
* Class::initSPropsImpl() const {
759 assert(numStaticProperties() > 0);
760 assert(getSPropData() == nullptr);
761 // Create an array that is initially large enough to hold all static
763 TypedValue
* const spropTable
=
764 new (request_arena()) TypedValue
[m_staticProperties
.size()];
766 boost::optional
<NameValueTable
> nvt
;
767 const bool hasNonscalarInit
= !m_sinitVec
.empty();
768 if (hasNonscalarInit
) {
769 nvt
= boost::in_place
<NameValueTable
>(m_staticProperties
.size());
772 // Iteratively initialize properties. Non-scalar initializers are
773 // initialized to KindOfUninit here, and the 86sinit()-based initialization
774 // finishes the job later.
775 for (Slot slot
= 0; slot
< m_staticProperties
.size(); ++slot
) {
776 const SProp
& sProp
= m_staticProperties
[slot
];
778 TypedValue
* storage
= 0;
779 if (sProp
.m_class
== this) {
780 // Embed static property value directly in array.
781 assert(tvIsStatic(&sProp
.m_val
));
782 spropTable
[slot
] = sProp
.m_val
;
783 storage
= &spropTable
[slot
];
785 // Alias parent class's static property.
786 bool visible
, accessible
;
787 storage
= sProp
.m_class
->getSProp(nullptr, sProp
.m_name
, visible
,
789 tvBindIndirect(&spropTable
[slot
], storage
);
792 if (hasNonscalarInit
) {
793 nvt
->migrateSet(sProp
.m_name
, storage
);
797 // Invoke 86sinit's if necessary, to handle non-scalar initializers.
798 if (hasNonscalarInit
) {
799 // See note in initPropsImpl for why its ok to allocate
800 // this on the stack.
801 NameValueTableWrapper
nvtWrapper(&*nvt
);
802 nvtWrapper
.incRefCount();
804 ArrayData
* args
= ArrayData::Make(1);
808 Variant
arg0(&nvtWrapper
);
809 args
= args
->appendRef(arg0
, false);
811 for (unsigned i
= 0; i
< m_sinitVec
.size(); i
++) {
813 g_vmContext
->invokeFunc(&retval
, m_sinitVec
[i
], args
, nullptr,
814 const_cast<Class
*>(this));
815 assert(!IS_REFCOUNTED_TYPE(retval
.m_type
));
818 // Release the args array. nvtWrapper is on the stack, so it
819 // better have a single reference.
820 assert(args
->getCount() == 1);
822 assert(nvtWrapper
.getCount() == 1);
824 assert(args
->getCount() == 1);
826 assert(nvtWrapper
.getCount() == 1);
834 TypedValue
* Class::getSProp(Class
* ctx
, const StringData
* sPropName
,
835 bool& visible
, bool& accessible
) const {
839 Slot sPropInd
= lookupSProp(sPropName
);
840 if (sPropInd
== kInvalidSlot
) {
841 // Non-existant property.
849 // Property access is from within a method of this class, so the property
853 Attr sPropAttrs
= m_staticProperties
[sPropInd
].m_attrs
;
854 if ((ctx
!= nullptr) && (classof(ctx
) || ctx
->classof(this))) {
855 // Property access is from within a parent class's method, which is
856 // allowed for protected/public properties.
857 switch (sPropAttrs
& (AttrPublic
|AttrProtected
|AttrPrivate
)) {
859 case AttrProtected
: accessible
= true; break;
861 accessible
= g_vmContext
->getDebuggerBypassCheck(); break;
862 default: not_reached();
865 // Property access is in an effectively anonymous context, so only public
866 // properties are accessible.
867 switch (sPropAttrs
& (AttrPublic
|AttrProtected
|AttrPrivate
)) {
868 case AttrPublic
: accessible
= true; break;
871 accessible
= g_vmContext
->getDebuggerBypassCheck(); break;
872 default: not_reached();
877 assert(sProps
!= nullptr);
878 TypedValue
* sProp
= tvDerefIndirect(&sProps
[sPropInd
]);
879 assert(sProp
->m_type
!= KindOfUninit
&&
880 "static property initialization failed to initialize a property");
884 bool Class::IsPropAccessible(const Prop
& prop
, Class
* ctx
) {
885 if (prop
.m_attrs
& AttrPublic
) return true;
886 if (prop
.m_attrs
& AttrPrivate
) return prop
.m_class
== ctx
;
887 if (!ctx
) return false;
889 return prop
.m_class
->classof(ctx
) || ctx
->classof(prop
.m_class
);
892 TypedValue
Class::getStaticPropInitVal(const SProp
& prop
) {
893 Class
* declCls
= prop
.m_class
;
894 Slot s
= declCls
->m_staticProperties
.findIndex(prop
.m_name
);
895 assert(s
!= kInvalidSlot
);
896 return declCls
->m_staticProperties
[s
].m_val
;
899 HphpArray
* Class::initClsCnsData() const {
900 Slot nConstants
= m_constants
.size();
901 HphpArray
* constants
= ArrayData::Make(nConstants
);
902 constants
->incRefCount();
904 if (m_parent
.get() != nullptr) {
905 if (g_vmContext
->getClsCnsData(m_parent
.get()) == nullptr) {
906 // Initialize recursively up the inheritance chain.
907 m_parent
->initClsCnsData();
911 for (Slot i
= 0; i
< nConstants
; ++i
) {
912 const Const
& constant
= m_constants
[i
];
913 const TypedValue
* tv
= &constant
.m_val
;
914 constants
->set((StringData
*)constant
.m_name
, tvAsCVarRef(tv
), false);
915 // XXX: set() converts KindOfUninit to KindOfNull, but our class
916 // constant logic needs to store KindOfUninit to indicate the the
917 // constant's value has not been computed yet. We should find a better
918 // way to deal with this.
919 if (tv
->m_type
== KindOfUninit
) {
920 constants
->nvGetValueRef(i
)->m_type
= KindOfUninit
;
924 g_vmContext
->setClsCnsData(this, constants
);
928 TypedValue
* Class::cnsNameToTV(const StringData
* clsCnsName
,
929 Slot
& clsCnsInd
) const {
930 clsCnsInd
= m_constants
.findIndex(clsCnsName
);
931 if (clsCnsInd
== kInvalidSlot
) {
934 return const_cast<TypedValue
*>(&m_constants
[clsCnsInd
].m_val
);
937 TypedValue
* Class::clsCnsGet(const StringData
* clsCnsName
) const {
939 TypedValue
* clsCns
= cnsNameToTV(clsCnsName
, clsCnsInd
);
940 if (!clsCns
|| clsCns
->m_type
!= KindOfUninit
) {
944 // This constant has a non-scalar initializer, so look in g_vmContext for
945 // an entry associated with this class.
946 HphpArray
* clsCnsData
= g_vmContext
->getClsCnsData(this);
947 if (clsCnsData
== nullptr) {
948 clsCnsData
= initClsCnsData();
951 clsCns
= clsCnsData
->nvGetValueRef(clsCnsInd
);
952 if (clsCns
->m_type
== KindOfUninit
) {
953 // The class constant has not been initialized yet; do so.
954 static StringData
* sd86cinit
= StringData::GetStaticString("86cinit");
955 const Func
* meth86cinit
=
956 m_constants
[clsCnsInd
].m_class
->lookupMethod(sd86cinit
);
958 tv
->m_data
.pstr
= (StringData
*)clsCnsName
;
959 tv
->m_type
= KindOfString
;
960 clsCnsName
->incRefCount();
961 g_vmContext
->invokeFuncFew(clsCns
, meth86cinit
, ActRec::encodeClass(this),
967 DataType
Class::clsCnsType(const StringData
* cnsName
) const {
969 TypedValue
* cns
= cnsNameToTV(cnsName
, slot
);
970 // TODO: lookup the constant in target cache in case it's dynamic
971 // and already initialized.
972 if (!cns
) return KindOfUninit
;
976 void Class::setParent() {
977 // Validate the parent
978 if (m_parent
.get() != nullptr) {
979 Attr attrs
= m_parent
->attrs();
980 if (UNLIKELY(attrs
& (AttrFinal
| AttrInterface
| AttrTrait
))) {
981 static StringData
* sd___MockClass
=
982 StringData::GetStaticString("__MockClass");
983 if (!(attrs
& AttrFinal
) ||
984 m_preClass
->userAttributes().find(sd___MockClass
) ==
985 m_preClass
->userAttributes().end()) {
986 raise_error("Class %s may not inherit from %s (%s)",
987 m_preClass
->name()->data(),
988 ((attrs
& AttrFinal
) ? "final class" :
989 (attrs
& AttrInterface
) ? "interface" : "trait"),
990 m_parent
->name()->data());
994 // Cache m_preClass->attrs()
995 m_attrCopy
= m_preClass
->attrs();
996 // Handle stuff specific to cppext classes
997 if (m_preClass
->instanceCtor()) {
998 m_InstanceCtor
= m_preClass
->instanceCtor();
999 m_builtinPropSize
= m_preClass
->builtinPropSize();
1000 m_clsInfo
= ClassInfo::FindSystemClassInterfaceOrTrait(nameRef());
1001 } else if (m_parent
.get()) {
1002 m_InstanceCtor
= m_parent
->m_InstanceCtor
;
1003 m_builtinPropSize
= m_parent
->m_builtinPropSize
;
1007 static Func
* findSpecialMethod(Class
* cls
, const StringData
* name
) {
1008 if (!cls
->preClass()->hasMethod(name
)) return nullptr;
1009 Func
* f
= cls
->preClass()->lookupMethod(name
);
1014 f
->setHasPrivateAncestor(false);
1018 void Class::setSpecial() {
1019 static StringData
* sd_toString
= StringData::GetStaticString("__toString");
1020 static StringData
* sd_uuconstruct
=
1021 StringData::GetStaticString("__construct");
1022 static StringData
* sd_uudestruct
=
1023 StringData::GetStaticString("__destruct");
1025 m_toString
= lookupMethod(sd_toString
);
1026 m_dtor
= lookupMethod(sd_uudestruct
);
1028 // Look for __construct() declared in either this class or a trait
1029 Func
* fConstruct
= lookupMethod(sd_uuconstruct
);
1030 if (fConstruct
&& (fConstruct
->preClass() == m_preClass
.get() ||
1031 fConstruct
->preClass()->attrs() & AttrTrait
)) {
1032 m_ctor
= fConstruct
;
1036 if (!(attrs() & AttrTrait
)) {
1037 // Look for Foo::Foo() declared in this class (cannot be via trait).
1038 Func
* fNamedCtor
= lookupMethod(m_preClass
->name());
1039 if (fNamedCtor
&& fNamedCtor
->preClass() == m_preClass
.get() &&
1040 !(fNamedCtor
->attrs() & AttrTrait
)) {
1042 Note: AttrTrait was set by the emitter if hphpc inlined a trait
1043 method into a class (WholeProgram mode only), so that we dont
1044 accidently mark it as a constructor here
1046 m_ctor
= fNamedCtor
;
1051 // Look for parent constructor other than 86ctor().
1052 if (m_parent
.get() != nullptr &&
1053 m_parent
->m_ctor
->name() != sd86ctor
) {
1054 m_ctor
= m_parent
->m_ctor
;
1058 // Use 86ctor(), since no program-supplied constructor exists
1059 m_ctor
= findSpecialMethod(this, sd86ctor
);
1060 assert(m_ctor
&& "class had no user-defined constructor or 86ctor");
1061 assert((m_ctor
->attrs() & ~AttrBuiltin
) ==
1062 (AttrPublic
|AttrNoInjection
|AttrPhpLeafFn
));
1065 void Class::applyTraitPrecRule(const PreClass::TraitPrecRule
& rule
,
1066 MethodToTraitListMap
& importMethToTraitMap
) {
1067 const StringData
* methName
= rule
.getMethodName();
1068 const StringData
* selectedTraitName
= rule
.getSelectedTraitName();
1069 TraitNameSet otherTraitNames
;
1070 rule
.getOtherTraitNames(otherTraitNames
);
1072 auto methIter
= importMethToTraitMap
.find(methName
);
1073 if (methIter
== importMethToTraitMap
.end()) {
1074 raise_error("unknown method '%s'", methName
->data());
1077 bool foundSelectedTrait
= false;
1079 TraitMethodList
&methList
= methIter
->second
;
1080 for (TraitMethodList::iterator nextTraitIter
= methList
.begin();
1081 nextTraitIter
!= methList
.end(); ) {
1082 TraitMethodList::iterator traitIter
= nextTraitIter
++;
1083 const StringData
* availTraitName
= traitIter
->m_trait
->name();
1084 if (availTraitName
== selectedTraitName
) {
1085 foundSelectedTrait
= true;
1087 if (otherTraitNames
.find(availTraitName
) != otherTraitNames
.end()) {
1088 otherTraitNames
.erase(availTraitName
);
1089 methList
.erase(traitIter
);
1094 // Check error conditions
1095 if (!foundSelectedTrait
) {
1096 raise_error("unknown trait '%s'", selectedTraitName
->data());
1098 if (otherTraitNames
.size()) {
1099 raise_error("unknown trait '%s'", (*otherTraitNames
.begin())->data());
1103 Class
* Class::findSingleTraitWithMethod(const StringData
* methName
) {
1104 // Note: m_methods includes methods from parents / traits recursively
1105 Class
* traitCls
= nullptr;
1106 for (auto const& t
: m_usedTraits
) {
1107 if (t
->m_methods
.contains(methName
)) {
1108 if (traitCls
!= nullptr) { // more than one trait contains method
1117 void Class::setImportTraitMethodModifiers(TraitMethodList
& methList
,
1120 for (TraitMethodList::iterator iter
= methList
.begin();
1121 iter
!= methList
.end(); iter
++) {
1122 if (iter
->m_trait
== traitCls
) {
1123 iter
->m_modifiers
= modifiers
;
1129 // Keep track of trait aliases in the class to support
1130 // ReflectionClass::getTraitAliases
1131 void Class::addTraitAlias(const StringData
* traitName
,
1132 const StringData
* origMethName
,
1133 const StringData
* newMethName
) {
1134 char buf
[traitName
->size() + origMethName
->size() + 9];
1135 sprintf(buf
, "%s::%s", (traitName
->empty() ? "(null)" : traitName
->data()),
1136 origMethName
->data());
1137 const StringData
* origName
= StringData::GetStaticString(buf
);
1138 m_traitAliases
.push_back(std::pair
<const StringData
*, const StringData
*>
1139 (newMethName
, origName
));
1142 void Class::applyTraitAliasRule(const PreClass::TraitAliasRule
& rule
,
1143 MethodToTraitListMap
& importMethToTraitMap
) {
1144 const StringData
* traitName
= rule
.getTraitName();
1145 const StringData
* origMethName
= rule
.getOrigMethodName();
1146 const StringData
* newMethName
= rule
.getNewMethodName();
1148 Class
* traitCls
= nullptr;
1149 if (traitName
->empty()) {
1150 traitCls
= findSingleTraitWithMethod(origMethName
);
1152 traitCls
= Unit::loadClass(traitName
);
1155 if (!traitCls
|| (!(traitCls
->attrs() & AttrTrait
))) {
1156 raise_error("unknown trait '%s'", traitName
->data());
1159 // Save info to support ReflectionClass::getTraitAliases
1160 addTraitAlias(traitName
, origMethName
, newMethName
);
1162 Func
* traitMeth
= traitCls
->lookupMethod(origMethName
);
1164 raise_error("unknown trait method '%s'", origMethName
->data());
1168 if (origMethName
== newMethName
) {
1169 ruleModifiers
= rule
.getModifiers();
1170 setImportTraitMethodModifiers(importMethToTraitMap
[origMethName
],
1171 traitCls
, ruleModifiers
);
1173 ruleModifiers
= rule
.getModifiers();
1174 TraitMethod
traitMethod(traitCls
, traitMeth
, ruleModifiers
);
1175 if (!Func::isSpecial(newMethName
)) {
1176 importMethToTraitMap
[newMethName
].push_back(traitMethod
);
1179 if (ruleModifiers
& AttrStatic
) {
1180 raise_error("cannot use 'static' as access modifier");
1184 void Class::applyTraitRules(MethodToTraitListMap
& importMethToTraitMap
) {
1185 for (size_t i
= 0; i
< m_preClass
->traitPrecRules().size(); i
++) {
1186 applyTraitPrecRule(m_preClass
->traitPrecRules()[i
],
1187 importMethToTraitMap
);
1189 for (size_t i
= 0; i
< m_preClass
->traitAliasRules().size(); i
++) {
1190 applyTraitAliasRule(m_preClass
->traitAliasRules()[i
],
1191 importMethToTraitMap
);
1195 void Class::importTraitMethod(const TraitMethod
& traitMethod
,
1196 const StringData
* methName
,
1197 MethodMap::Builder
& builder
) {
1198 Func
* method
= traitMethod
.m_method
;
1199 Attr modifiers
= traitMethod
.m_modifiers
;
1201 MethodMap::Builder::iterator mm_iter
= builder
.find(methName
);
1202 // For abstract methods, simply return if method already declared
1203 if ((modifiers
& AttrAbstract
) && mm_iter
!= builder
.end()) {
1207 if (modifiers
== AttrNone
) {
1208 modifiers
= method
->attrs();
1210 // Trait alias statements are only allowed to change the attributes that
1211 // are part 'attrMask' below; all other method attributes are preserved
1212 Attr attrMask
= (Attr
)(AttrPublic
| AttrProtected
| AttrPrivate
|
1213 AttrAbstract
| AttrFinal
);
1214 modifiers
= (Attr
)((modifiers
& (attrMask
)) |
1215 (method
->attrs() & ~(attrMask
)));
1218 Func
* parentMethod
= nullptr;
1219 if (mm_iter
!= builder
.end()) {
1220 Func
* existingMethod
= builder
[mm_iter
->second
];
1221 if (existingMethod
->cls() == this) {
1222 // Don't override an existing method if this class provided an
1226 parentMethod
= existingMethod
;
1228 Func
* f
= method
->clone();
1230 f
->setClsAndName(this, methName
);
1231 f
->setAttrs(modifiers
);
1232 if (!parentMethod
) {
1234 builder
.add(methName
, f
);
1235 f
->setBaseCls(this);
1236 f
->setHasPrivateAncestor(false);
1238 // Override an existing method
1241 methodOverrideCheck(parentMethod
, f
);
1243 assert(!(f
->attrs() & AttrPrivate
) ||
1244 (parentMethod
->attrs() & AttrPrivate
));
1245 if ((parentMethod
->attrs() & AttrPrivate
) || (f
->attrs() & AttrPrivate
)) {
1248 baseClass
= parentMethod
->baseCls();
1250 f
->setBaseCls(baseClass
);
1251 f
->setHasPrivateAncestor(
1252 parentMethod
->hasPrivateAncestor() ||
1253 (parentMethod
->attrs() & AttrPrivate
));
1254 builder
[mm_iter
->second
] = f
;
1258 // This method removes trait abstract methods that are either:
1259 // 1) implemented by other traits
1261 void Class::removeSpareTraitAbstractMethods(
1262 MethodToTraitListMap
& importMethToTraitMap
) {
1264 for (MethodToTraitListMap::iterator iter
= importMethToTraitMap
.begin();
1265 iter
!= importMethToTraitMap
.end(); iter
++) {
1267 TraitMethodList
& tMethList
= iter
->second
;
1268 bool hasNonAbstractMeth
= false;
1269 unsigned countAbstractMeths
= 0;
1270 for (TraitMethodList::const_iterator traitMethIter
= tMethList
.begin();
1271 traitMethIter
!= tMethList
.end(); traitMethIter
++) {
1272 if (!(traitMethIter
->m_modifiers
& AttrAbstract
)) {
1273 hasNonAbstractMeth
= true;
1275 countAbstractMeths
++;
1278 if (hasNonAbstractMeth
|| countAbstractMeths
> 1) {
1279 // Erase spare abstract declarations
1280 bool firstAbstractMeth
= true;
1281 for (TraitMethodList::iterator nextTraitIter
= tMethList
.begin();
1282 nextTraitIter
!= tMethList
.end(); ) {
1283 TraitMethodList::iterator traitIter
= nextTraitIter
++;
1284 if (traitIter
->m_modifiers
& AttrAbstract
) {
1285 if (hasNonAbstractMeth
|| !firstAbstractMeth
) {
1286 tMethList
.erase(traitIter
);
1288 firstAbstractMeth
= false;
1296 void Class::importTraitMethods(MethodMap::Builder
& builder
) {
1297 MethodToTraitListMap importMethToTraitMap
;
1299 // 1. Find all methods to be imported
1300 for (auto const& t
: m_usedTraits
) {
1301 Class
* trait
= t
.get();
1302 for (Slot i
= 0; i
< trait
->m_methods
.size(); ++i
) {
1303 Func
* method
= trait
->m_methods
[i
];
1304 const StringData
* methName
= method
->name();
1305 TraitMethod
traitMethod(trait
, method
, method
->attrs());
1306 if (!Func::isSpecial(methName
)) {
1307 importMethToTraitMap
[methName
].push_back(traitMethod
);
1312 // 2. Apply trait rules
1313 applyTraitRules(importMethToTraitMap
);
1315 // 3. Remove abstract methods provided by other traits, and also duplicates
1316 removeSpareTraitAbstractMethods(importMethToTraitMap
);
1318 // 4. Actually import the methods
1319 for (MethodToTraitListMap::const_iterator iter
=
1320 importMethToTraitMap
.begin();
1321 iter
!= importMethToTraitMap
.end(); iter
++) {
1323 // The rules may rule out a method from all traits.
1324 // In this case, simply don't import the method.
1325 if (iter
->second
.size() == 0) {
1329 // Consistency checking: each name must only refer to one imported method
1330 if (iter
->second
.size() > 1) {
1331 // OK if the class will override the method...
1332 if (m_preClass
->hasMethod(iter
->first
)) continue;
1334 raise_error("method '%s' declared in multiple traits",
1335 iter
->first
->data());
1338 TraitMethodList::const_iterator traitMethIter
= iter
->second
.begin();
1339 importTraitMethod(*traitMethIter
, iter
->first
, builder
);
1344 void Class::methodOverrideCheck(const Func
* parentMethod
, const Func
* method
) {
1345 // Skip special methods
1346 if (method
->isGenerated()) return;
1348 if ((parentMethod
->attrs() & AttrFinal
)) {
1349 static StringData
* sd___MockClass
=
1350 StringData::GetStaticString("__MockClass");
1351 if (m_preClass
->userAttributes().find(sd___MockClass
) ==
1352 m_preClass
->userAttributes().end()) {
1353 raise_error("Cannot override final method %s::%s()",
1354 m_parent
->name()->data(), parentMethod
->name()->data());
1358 if (method
->attrs() & AttrAbstract
) {
1359 raise_error("Cannot re-declare %sabstract method %s::%s() abstract in "
1361 (parentMethod
->attrs() & AttrAbstract
) ? "" : "non-",
1362 m_parent
->m_preClass
->name()->data(),
1363 parentMethod
->name()->data(), m_preClass
->name()->data());
1366 if ((method
->attrs() & (AttrPublic
| AttrProtected
| AttrPrivate
)) >
1367 (parentMethod
->attrs() & (AttrPublic
| AttrProtected
| AttrPrivate
))) {
1369 "Access level to %s::%s() must be %s (as in class %s) or weaker",
1370 m_preClass
->name()->data(), method
->name()->data(),
1371 attrToVisibilityStr(parentMethod
->attrs()),
1372 m_parent
->name()->data());
1375 if ((method
->attrs() & AttrStatic
) != (parentMethod
->attrs() & AttrStatic
)) {
1376 raise_error("Cannot change %sstatic method %s::%s() to %sstatic in %s",
1377 (parentMethod
->attrs() & AttrStatic
) ? "" : "non-",
1378 parentMethod
->baseCls()->name()->data(),
1379 method
->name()->data(),
1380 (method
->attrs() & AttrStatic
) ? "" : "non-",
1381 m_preClass
->name()->data());
1384 Func
* baseMethod
= parentMethod
->baseCls()->lookupMethod(method
->name());
1385 if (!(method
->attrs() & AttrAbstract
) &&
1386 (baseMethod
->attrs() & AttrAbstract
) &&
1387 (!hphpiCompat
|| strcmp(method
->name()->data(), "__construct"))) {
1388 method
->parametersCompat(m_preClass
.get(), baseMethod
);
1392 void Class::setMethods() {
1393 std::vector
<Slot
> parentMethodsWithStaticLocals
;
1394 MethodMap::Builder builder
;
1396 if (m_parent
.get() != nullptr) {
1397 // Copy down the parent's method entries. These may be overridden below.
1398 for (Slot i
= 0; i
< m_parent
->m_methods
.size(); ++i
) {
1399 Func
* f
= m_parent
->m_methods
[i
];
1401 if ((f
->attrs() & AttrClone
) ||
1402 (!(f
->attrs() & AttrPrivate
) && f
->hasStaticLocals())) {
1403 // When copying down an entry for a non-private method that has
1404 // static locals, we want to make a copy of the Func so that it
1405 // gets a distinct set of static locals variables. We defer making
1406 // a copy of the parent method until the end because it might get
1408 parentMethodsWithStaticLocals
.push_back(i
);
1410 assert(builder
.size() == i
);
1411 builder
.add(f
->name(), f
);
1415 assert(AttrPublic
< AttrProtected
&& AttrProtected
< AttrPrivate
);
1416 // Overlay/append this class's public/protected methods onto/to those of the
1418 for (size_t methI
= 0; methI
< m_preClass
->numMethods(); ++methI
) {
1419 Func
* method
= m_preClass
->methods()[methI
];
1420 if (Func::isSpecial(method
->name())) {
1421 if (method
->name() == sd86ctor
||
1422 method
->name() == sd86sinit
||
1423 method
->name() == sd86pinit
) {
1425 * we could also skip the cinit function here, but
1426 * that would mean storing it somewhere else.
1431 MethodMap::Builder::iterator it2
= builder
.find(method
->name());
1432 if (it2
!= builder
.end()) {
1433 Func
* parentMethod
= builder
[it2
->second
];
1434 // We should never have null func pointers to deal with
1435 assert(parentMethod
);
1436 methodOverrideCheck(parentMethod
, method
);
1438 Func
* f
= method
->clone();
1442 assert(!(f
->attrs() & AttrPrivate
) ||
1443 (parentMethod
->attrs() & AttrPrivate
));
1444 if ((parentMethod
->attrs() & AttrPrivate
) || (f
->attrs() & AttrPrivate
)) {
1447 baseClass
= parentMethod
->baseCls();
1449 f
->setBaseCls(baseClass
);
1450 f
->setHasPrivateAncestor(
1451 parentMethod
->hasPrivateAncestor() ||
1452 (parentMethod
->attrs() & AttrPrivate
));
1453 builder
[it2
->second
] = f
;
1455 // This is the first class that declares the method
1456 Class
* baseClass
= this;
1458 Func
* f
= method
->clone();
1461 f
->setBaseCls(baseClass
);
1462 f
->setHasPrivateAncestor(false);
1463 builder
.add(method
->name(), f
);
1467 m_traitsBeginIdx
= builder
.size();
1468 if (m_usedTraits
.size()) {
1469 importTraitMethods(builder
);
1471 m_traitsEndIdx
= builder
.size();
1473 // Make copies of Funcs inherited from the parent class that have
1475 std::vector
<Slot
>::const_iterator it
;
1476 for (it
= parentMethodsWithStaticLocals
.begin();
1477 it
!= parentMethodsWithStaticLocals
.end(); ++it
) {
1478 Func
*& f
= builder
[*it
];
1479 if (f
->cls() != this) {
1480 // Don't update f's m_cls if it doesn't have AttrClone set:
1481 // we're cloning it so that we get a distinct set of static
1482 // locals and a separate translation, not a different context
1485 if (f
->attrs() & AttrClone
) {
1492 // If class is not abstract, check that all abstract methods have been defined
1493 if (!(attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
))) {
1494 for (Slot i
= 0; i
< builder
.size(); i
++) {
1495 const Func
* meth
= builder
[i
];
1496 if (meth
->attrs() & AttrAbstract
) {
1497 raise_error("Class %s contains abstract method (%s) and "
1498 "must therefore be declared abstract or implement "
1499 "the remaining methods", m_preClass
->name()->data(),
1500 meth
->name()->data());
1505 m_methods
.create(builder
);
1506 for (Slot i
= 0; i
< m_methods
.size(); ++i
) {
1507 m_methods
[i
]->setMethodSlot(i
);
1511 void Class::setODAttributes() {
1512 static StringData
* sd__sleep
= StringData::GetStaticString("__sleep");
1513 static StringData
* sd__get
= StringData::GetStaticString("__get");
1514 static StringData
* sd__set
= StringData::GetStaticString("__set");
1515 static StringData
* sd__isset
= StringData::GetStaticString("__isset");
1516 static StringData
* sd__unset
= StringData::GetStaticString("__unset");
1517 static StringData
* sd__call
= StringData::GetStaticString("__call");
1518 static StringData
* sd__callStatic
1519 = StringData::GetStaticString("__callStatic");
1522 if (lookupMethod(sd__sleep
)) { m_ODAttrs
|= ObjectData::HasSleep
; }
1523 if (lookupMethod(sd__get
)) { m_ODAttrs
|= ObjectData::UseGet
; }
1524 if (lookupMethod(sd__set
)) { m_ODAttrs
|= ObjectData::UseSet
; }
1525 if (lookupMethod(sd__isset
)) { m_ODAttrs
|= ObjectData::UseIsset
; }
1526 if (lookupMethod(sd__unset
)) { m_ODAttrs
|= ObjectData::UseUnset
; }
1527 if (lookupMethod(sd__call
)) { m_ODAttrs
|= ObjectData::HasCall
; }
1528 if (lookupMethod(sd__callStatic
)) { m_ODAttrs
|= ObjectData::HasCallStatic
; }
1531 void Class::setConstants() {
1532 ConstMap::Builder builder
;
1534 if (m_parent
.get() != nullptr) {
1535 for (Slot i
= 0; i
< m_parent
->m_constants
.size(); ++i
) {
1536 // Copy parent's constants.
1537 builder
.add(m_parent
->m_constants
[i
].m_name
, m_parent
->m_constants
[i
]);
1541 // Copy in interface constants.
1542 for (auto it
= m_declInterfaces
.begin(); it
!= m_declInterfaces
.end(); ++it
) {
1543 for (Slot slot
= 0; slot
< (*it
)->m_constants
.size(); ++slot
) {
1544 const Const
& iConst
= (*it
)->m_constants
[slot
];
1546 // If you're inheriting a constant with the same name as an
1547 // existing one, they must originate from the same place.
1548 ConstMap::Builder::iterator existing
= builder
.find(iConst
.m_name
);
1549 if (existing
!= builder
.end() &&
1550 builder
[existing
->second
].m_class
!= iConst
.m_class
) {
1551 raise_error("Cannot inherit previously-inherited constant %s",
1552 iConst
.m_name
->data());
1555 builder
.add(iConst
.m_name
, iConst
);
1559 for (Slot i
= 0, sz
= m_preClass
->numConstants(); i
< sz
; ++i
) {
1560 const PreClass::Const
* preConst
= &m_preClass
->constants()[i
];
1561 ConstMap::Builder::iterator it2
= builder
.find(preConst
->name());
1562 if (it2
!= builder
.end()) {
1563 if (!(builder
[it2
->second
].m_class
->attrs() & AttrInterface
)) {
1564 // Overlay ancestor's constant, only if it was not an interface const.
1565 builder
[it2
->second
].m_class
= this;
1566 builder
[it2
->second
].m_val
= preConst
->val();
1568 raise_error("Cannot override previously defined constant %s::%s in %s",
1569 builder
[it2
->second
].m_class
->name()->data(),
1570 preConst
->name()->data(),
1571 m_preClass
->name()->data());
1576 constant
.m_class
= this;
1577 constant
.m_name
= preConst
->name();
1578 constant
.m_val
= preConst
->val();
1579 constant
.m_phpCode
= preConst
->phpCode();
1580 builder
.add(preConst
->name(), constant
);
1584 m_constants
.create(builder
);
1587 static void copyDeepInitAttr(const PreClass::Prop
* pclsProp
,
1588 Class::Prop
* clsProp
) {
1589 if (pclsProp
->attrs() & AttrDeepInit
) {
1590 clsProp
->m_attrs
= (Attr
)(clsProp
->m_attrs
| AttrDeepInit
);
1592 clsProp
->m_attrs
= (Attr
)(clsProp
->m_attrs
& ~AttrDeepInit
);
1596 void Class::setProperties() {
1597 int numInaccessible
= 0;
1598 PropMap::Builder curPropMap
;
1599 SPropMap::Builder curSPropMap
;
1600 m_hasDeepInitProps
= false;
1602 if (m_parent
.get() != nullptr) {
1603 // m_hasDeepInitProps indicates if there are properties that require
1604 // deep initialization. Note there are cases where m_hasDeepInitProps is
1605 // true but none of the properties require deep initialization; this can
1606 // happen if a derived class redeclares a public or protected property
1607 // from an ancestor class. We still get correct behavior in these cases,
1608 // so it works out okay.
1609 m_hasDeepInitProps
= m_parent
->m_hasDeepInitProps
;
1610 for (Slot slot
= 0; slot
< m_parent
->m_declProperties
.size(); ++slot
) {
1611 const Prop
& parentProp
= m_parent
->m_declProperties
[slot
];
1613 // Copy parent's declared property. Protected properties may be
1614 // weakened to public below, but otherwise, the parent's properties
1615 // will stay the same for this class.
1617 prop
.m_class
= parentProp
.m_class
;
1618 prop
.m_mangledName
= parentProp
.m_mangledName
;
1619 prop
.m_originalMangledName
= parentProp
.m_originalMangledName
;
1620 prop
.m_attrs
= parentProp
.m_attrs
;
1621 prop
.m_docComment
= parentProp
.m_docComment
;
1622 prop
.m_typeConstraint
= parentProp
.m_typeConstraint
;
1623 prop
.m_name
= parentProp
.m_name
;
1624 prop
.m_hphpcType
= parentProp
.m_hphpcType
;
1625 if (!(parentProp
.m_attrs
& AttrPrivate
)) {
1626 curPropMap
.add(prop
.m_name
, prop
);
1629 curPropMap
.addUnnamed(prop
);
1632 m_declPropInit
= m_parent
->m_declPropInit
;
1633 for (Slot slot
= 0; slot
< m_parent
->m_staticProperties
.size(); ++slot
) {
1634 const SProp
& parentProp
= m_parent
->m_staticProperties
[slot
];
1635 if (parentProp
.m_attrs
& AttrPrivate
) continue;
1637 // Alias parent's static property.
1639 sProp
.m_name
= parentProp
.m_name
;
1640 sProp
.m_attrs
= parentProp
.m_attrs
;
1641 sProp
.m_typeConstraint
= parentProp
.m_typeConstraint
;
1642 sProp
.m_docComment
= parentProp
.m_docComment
;
1643 sProp
.m_class
= parentProp
.m_class
;
1644 tvWriteUninit(&sProp
.m_val
);
1645 curSPropMap
.add(sProp
.m_name
, sProp
);
1649 assert(AttrPublic
< AttrProtected
&& AttrProtected
< AttrPrivate
);
1650 for (Slot slot
= 0; slot
< m_preClass
->numProperties(); ++slot
) {
1651 const PreClass::Prop
* preProp
= &m_preClass
->properties()[slot
];
1653 if (!(preProp
->attrs() & AttrStatic
)) {
1654 // Overlay/append this class's protected and public properties onto/to
1655 // those of the parent, and append this class's private properties.
1656 // Append order doesn't matter here (unlike in setMethods()).
1657 // Prohibit static-->non-static redeclaration.
1658 SPropMap::Builder::iterator it2
= curSPropMap
.find(preProp
->name());
1659 if (it2
!= curSPropMap
.end()) {
1660 raise_error("Cannot redeclare static %s::$%s as non-static %s::$%s",
1661 curSPropMap
[it2
->second
].m_class
->name()->data(),
1662 preProp
->name()->data(), m_preClass
->name()->data(),
1663 preProp
->name()->data());
1665 // Get parent's equivalent property, if one exists.
1666 const Prop
* parentProp
= nullptr;
1667 if (m_parent
.get() != nullptr) {
1668 Slot id
= m_parent
->m_declProperties
.findIndex(preProp
->name());
1669 if (id
!= kInvalidSlot
) {
1670 parentProp
= &m_parent
->m_declProperties
[id
];
1673 // Prohibit strengthening.
1675 && (preProp
->attrs() & (AttrPublic
|AttrProtected
|AttrPrivate
))
1676 > (parentProp
->m_attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
))) {
1678 "Access level to %s::$%s() must be %s (as in class %s) or weaker",
1679 m_preClass
->name()->data(), preProp
->name()->data(),
1680 attrToVisibilityStr(parentProp
->m_attrs
),
1681 m_parent
->name()->data());
1683 if (preProp
->attrs() & AttrDeepInit
) {
1684 m_hasDeepInitProps
= true;
1686 switch (preProp
->attrs() & (AttrPublic
|AttrProtected
|AttrPrivate
)) {
1688 // Append a new private property.
1690 prop
.m_name
= preProp
->name();
1691 prop
.m_mangledName
= preProp
->mangledName();
1692 prop
.m_originalMangledName
= preProp
->mangledName();
1693 prop
.m_attrs
= preProp
->attrs();
1694 // This is the first class to declare this property
1695 prop
.m_class
= this;
1696 prop
.m_typeConstraint
= preProp
->typeConstraint();
1697 prop
.m_docComment
= preProp
->docComment();
1698 prop
.m_hphpcType
= preProp
->hphpcType();
1699 curPropMap
.add(preProp
->name(), prop
);
1700 m_declPropInit
.push_back(m_preClass
->lookupProp(preProp
->name())
1704 case AttrProtected
: {
1705 // Check whether a superclass has already declared this protected
1707 PropMap::Builder::iterator it2
= curPropMap
.find(preProp
->name());
1708 if (it2
!= curPropMap
.end()) {
1709 assert((curPropMap
[it2
->second
].m_attrs
1710 & (AttrPublic
|AttrProtected
|AttrPrivate
)) == AttrProtected
);
1711 const TypedValue
& tv
= m_preClass
->lookupProp(preProp
->name())->val();
1712 TypedValueAux
& tvaux
= m_declPropInit
[it2
->second
];
1713 tvaux
.m_data
= tv
.m_data
;
1714 tvaux
.m_type
= tv
.m_type
;
1715 copyDeepInitAttr(preProp
, &curPropMap
[it2
->second
]);
1718 // Append a new protected property.
1720 prop
.m_name
= preProp
->name();
1721 prop
.m_mangledName
= preProp
->mangledName();
1722 prop
.m_originalMangledName
= preProp
->mangledName();
1723 prop
.m_attrs
= preProp
->attrs();
1724 prop
.m_typeConstraint
= preProp
->typeConstraint();
1725 // This is the first class to declare this property
1726 prop
.m_class
= this;
1727 prop
.m_docComment
= preProp
->docComment();
1728 prop
.m_hphpcType
= preProp
->hphpcType();
1729 curPropMap
.add(preProp
->name(), prop
);
1730 m_declPropInit
.push_back(m_preClass
->lookupProp(preProp
->name())
1735 // Check whether a superclass has already declared this as a
1736 // protected/public property.
1737 PropMap::Builder::iterator it2
= curPropMap
.find(preProp
->name());
1738 if (it2
!= curPropMap
.end()) {
1739 Prop
& prop
= curPropMap
[it2
->second
];
1740 if ((prop
.m_attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
))
1742 // Weaken protected property to public.
1743 prop
.m_mangledName
= preProp
->mangledName();
1744 prop
.m_originalMangledName
= preProp
->mangledName();
1745 prop
.m_attrs
= Attr(prop
.m_attrs
^ (AttrProtected
|AttrPublic
));
1746 prop
.m_typeConstraint
= preProp
->typeConstraint();
1748 const TypedValue
& tv
= m_preClass
->lookupProp(preProp
->name())->val();
1749 TypedValueAux
& tvaux
= m_declPropInit
[it2
->second
];
1750 tvaux
.m_data
= tv
.m_data
;
1751 tvaux
.m_type
= tv
.m_type
;
1752 copyDeepInitAttr(preProp
, &curPropMap
[it2
->second
]);
1755 // Append a new public property.
1757 prop
.m_name
= preProp
->name();
1758 prop
.m_mangledName
= preProp
->mangledName();
1759 prop
.m_originalMangledName
= preProp
->mangledName();
1760 prop
.m_attrs
= preProp
->attrs();
1761 prop
.m_typeConstraint
= preProp
->typeConstraint();
1762 // This is the first class to declare this property
1763 prop
.m_class
= this;
1764 prop
.m_docComment
= preProp
->docComment();
1765 prop
.m_hphpcType
= preProp
->hphpcType();
1766 curPropMap
.add(preProp
->name(), prop
);
1767 m_declPropInit
.push_back(m_preClass
->lookupProp(preProp
->name())
1771 default: assert(false);
1773 } else { // Static property.
1774 // Prohibit non-static-->static redeclaration.
1775 PropMap::Builder::iterator it2
= curPropMap
.find(preProp
->name());
1776 if (it2
!= curPropMap
.end()) {
1777 // Find class that declared non-static property.
1779 for (ancestor
= m_parent
.get();
1780 !ancestor
->m_preClass
->hasProp(preProp
->name());
1781 ancestor
= ancestor
->m_parent
.get()) {
1783 raise_error("Cannot redeclare non-static %s::$%s as static %s::$%s",
1784 ancestor
->name()->data(),
1785 preProp
->name()->data(),
1786 m_preClass
->name()->data(),
1787 preProp
->name()->data());
1789 // Get parent's equivalent property, if one exists.
1790 SPropMap::Builder::iterator it3
= curSPropMap
.find(preProp
->name());
1791 Slot sPropInd
= kInvalidSlot
;
1792 // Prohibit strengthening.
1793 if (it3
!= curSPropMap
.end()) {
1794 const SProp
& parentSProp
= curSPropMap
[it3
->second
];
1795 if ((preProp
->attrs() & (AttrPublic
|AttrProtected
|AttrPrivate
))
1796 > (parentSProp
.m_attrs
& (AttrPublic
|AttrProtected
|AttrPrivate
))) {
1798 "Access level to %s::$%s() must be %s (as in class %s) or weaker",
1799 m_preClass
->name()->data(), preProp
->name()->data(),
1800 attrToVisibilityStr(parentSProp
.m_attrs
),
1801 m_parent
->name()->data());
1803 sPropInd
= it3
->second
;
1805 // Create a new property, or overlay ancestor's property if one exists.
1806 if (sPropInd
== kInvalidSlot
) {
1808 sProp
.m_name
= preProp
->name();
1809 sPropInd
= curSPropMap
.size();
1810 curSPropMap
.add(sProp
.m_name
, sProp
);
1812 SProp
& sProp
= curSPropMap
[sPropInd
];
1813 // Finish initializing.
1814 sProp
.m_attrs
= preProp
->attrs();
1815 sProp
.m_typeConstraint
= preProp
->typeConstraint();
1816 sProp
.m_docComment
= preProp
->docComment();
1817 sProp
.m_class
= this;
1818 sProp
.m_val
= m_preClass
->lookupProp(preProp
->name())->val();
1822 importTraitProps(curPropMap
, curSPropMap
);
1824 m_declProperties
.create(curPropMap
);
1825 m_staticProperties
.create(curSPropMap
);
1827 m_declPropNumAccessible
= m_declProperties
.size() - numInaccessible
;
1830 bool Class::compatibleTraitPropInit(TypedValue
& tv1
, TypedValue
& tv2
) {
1831 if (tv1
.m_type
!= tv2
.m_type
) return false;
1832 switch (tv1
.m_type
) {
1833 case KindOfNull
: return true;
1837 case KindOfStaticString
:
1839 return same(tvAsVariant(&tv1
), tvAsVariant(&tv2
));
1840 default: return false;
1844 void Class::importTraitInstanceProp(Class
* trait
,
1846 TypedValue
& traitPropVal
,
1847 PropMap::Builder
& curPropMap
) {
1848 PropMap::Builder::iterator prevIt
= curPropMap
.find(traitProp
.m_name
);
1850 if (prevIt
== curPropMap
.end()) {
1851 // New prop, go ahead and add it
1852 Prop prop
= traitProp
;
1853 prop
.m_class
= this; // set current class as the first declaring prop
1854 // private props' mangled names contain the class name, so regenerate them
1855 if (prop
.m_attrs
& AttrPrivate
) {
1856 prop
.m_mangledName
= PreClass::manglePropName(m_preClass
->name(),
1860 curPropMap
.add(prop
.m_name
, prop
);
1861 m_declPropInit
.push_back(traitPropVal
);
1863 // Redeclared prop, make sure it matches previous declarations
1864 Prop
& prevProp
= curPropMap
[prevIt
->second
];
1865 TypedValue
& prevPropVal
= m_declPropInit
[prevIt
->second
];
1866 if (prevProp
.m_attrs
!= traitProp
.m_attrs
||
1867 !compatibleTraitPropInit(prevPropVal
, traitPropVal
)) {
1868 raise_error("trait declaration of property '%s' is incompatible with "
1869 "previous declaration", traitProp
.m_name
->data());
1874 void Class::importTraitStaticProp(Class
* trait
,
1876 PropMap::Builder
& curPropMap
,
1877 SPropMap::Builder
& curSPropMap
) {
1878 // Check if prop already declared as non-static
1879 if (curPropMap
.find(traitProp
.m_name
) != curPropMap
.end()) {
1880 raise_error("trait declaration of property '%s' is incompatible with "
1881 "previous declaration", traitProp
.m_name
->data());
1884 SPropMap::Builder::iterator prevIt
= curSPropMap
.find(traitProp
.m_name
);
1885 if (prevIt
== curSPropMap
.end()) {
1886 // New prop, go ahead and add it
1887 SProp prop
= traitProp
;
1888 prop
.m_class
= this; // set current class as the first declaring prop
1889 curSPropMap
.add(prop
.m_name
, prop
);
1891 // Redeclared prop, make sure it matches previous declaration
1892 SProp
& prevProp
= curSPropMap
[prevIt
->second
];
1893 TypedValue prevPropVal
;
1894 if (prevProp
.m_class
== this) {
1895 // If this static property was declared by this class, we can
1896 // get the initial value directly from m_val
1897 prevPropVal
= prevProp
.m_val
;
1899 // If this static property was declared in a parent class, m_val
1900 // will be KindOfUninit, and we'll need to consult the appropriate
1901 // parent class to get the initial value.
1902 prevPropVal
= getStaticPropInitVal(prevProp
);
1904 if (prevProp
.m_attrs
!= traitProp
.m_attrs
||
1905 !compatibleTraitPropInit(traitProp
.m_val
, prevPropVal
)) {
1906 raise_error("trait declaration of property '%s' is incompatible with "
1907 "previous declaration", traitProp
.m_name
->data());
1909 prevProp
.m_class
= this;
1910 prevProp
.m_val
= prevPropVal
;
1914 void Class::importTraitProps(PropMap::Builder
& curPropMap
,
1915 SPropMap::Builder
& curSPropMap
) {
1916 if (attrs() & AttrNoExpandTrait
) return;
1917 for (auto& t
: m_usedTraits
) {
1918 Class
* trait
= t
.get();
1920 // instance properties
1921 for (Slot p
= 0; p
< trait
->m_declProperties
.size(); p
++) {
1922 Prop
& traitProp
= trait
->m_declProperties
[p
];
1923 TypedValue
& traitPropVal
= trait
->m_declPropInit
[p
];
1924 importTraitInstanceProp(trait
, traitProp
, traitPropVal
,
1928 // static properties
1929 for (Slot p
= 0; p
< trait
->m_staticProperties
.size(); ++p
) {
1930 SProp
& traitProp
= trait
->m_staticProperties
[p
];
1931 importTraitStaticProp(trait
, traitProp
, curPropMap
,
1937 void Class::addTraitPropInitializers(bool staticProps
) {
1938 if (attrs() & AttrNoExpandTrait
) return;
1939 for (unsigned t
= 0; t
< m_usedTraits
.size(); t
++) {
1940 Class
* trait
= m_usedTraits
[t
].get();
1941 InitVec
& traitInitVec
= staticProps
? trait
->m_sinitVec
: trait
->m_pinitVec
;
1942 InitVec
& thisInitVec
= staticProps
? m_sinitVec
: m_pinitVec
;
1943 // Insert trait's 86[ps]init into the current class, avoiding repetitions.
1944 for (unsigned m
= 0; m
< traitInitVec
.size(); m
++) {
1945 // Linear search, but these vectors shouldn't be big.
1946 if (find(thisInitVec
.begin(), thisInitVec
.end(), traitInitVec
[m
]) ==
1947 thisInitVec
.end()) {
1948 thisInitVec
.push_back(traitInitVec
[m
]);
1954 void Class::setInitializers() {
1955 if (m_parent
.get() != nullptr) {
1956 // Copy parent's 86pinit() vector, so that the 86pinit() methods can be
1957 // called in reverse order without any search/recursion during
1959 m_pinitVec
= m_parent
->m_pinitVec
;
1962 // This class only has a __[ps]init() method if it's needed. Append to the
1963 // vectors of __[ps]init() methods, so that reverse iteration of the vectors
1964 // runs this class's __[ps]init() first, in case multiple classes in the
1965 // hierarchy initialize the same property.
1966 const Func
* meth86pinit
= findSpecialMethod(this, sd86pinit
);
1967 if (meth86pinit
!= nullptr) {
1968 m_pinitVec
.push_back(meth86pinit
);
1970 addTraitPropInitializers(false);
1971 const Func
* sinit
= findSpecialMethod(this, sd86sinit
);
1973 m_sinitVec
.push_back(sinit
);
1975 addTraitPropInitializers(true);
1977 m_needInitialization
= (m_pinitVec
.size() > 0 ||
1978 m_staticProperties
.size() > 0);
1979 m_hasInitMethods
= (m_pinitVec
.size() > 0 || m_sinitVec
.size() > 0);
1981 // The __init__ method defined in the Exception class gets special treatment
1982 static StringData
* sd__init__
= StringData::GetStaticString("__init__");
1983 static StringData
* sd_exn
= StringData::GetStaticString("Exception");
1984 const Func
* einit
= lookupMethod(sd__init__
);
1985 m_callsCustomInstanceInit
=
1986 (einit
&& einit
->preClass()->name()->isame(sd_exn
));
1989 // Checks if interface methods are OK:
1990 // - there's no requirement if this is a trait, interface, or abstract class
1991 // - a non-abstract class must implement all methods from interfaces it
1992 // declares to implement (either directly or indirectly), arity must be
1993 // compatible (at least as many parameters, additional parameters must have
1994 // defaults), and typehints must be compatible
1995 void Class::checkInterfaceMethods() {
1996 for (int i
= 0, size
= m_interfaces
.size(); i
< size
; i
++) {
1997 const Class
* iface
= m_interfaces
[i
];
1999 for (size_t m
= 0; m
< iface
->m_methods
.size(); m
++) {
2000 Func
* imeth
= iface
->m_methods
[m
];
2001 const StringData
* methName
= imeth
->name();
2003 // Skip special methods
2004 if (Func::isSpecial(methName
)) continue;
2006 Func
* meth
= lookupMethod(methName
);
2008 if (attrs() & (AttrTrait
| AttrInterface
| AttrAbstract
)) {
2009 if (meth
== nullptr) {
2010 // Skip unimplemented method.
2014 // Verify that method is not abstract within concrete class.
2015 if (meth
== nullptr || (meth
->attrs() & AttrAbstract
)) {
2016 raise_error("Class %s contains abstract method (%s) and "
2017 "must therefore be declared abstract or implement "
2018 "the remaining methods", name()->data(),
2022 bool ifaceStaticMethod
= imeth
->attrs() & AttrStatic
;
2023 bool classStaticMethod
= meth
->attrs() & AttrStatic
;
2024 if (classStaticMethod
!= ifaceStaticMethod
) {
2025 raise_error("Cannot make %sstatic method %s::%s() %sstatic "
2027 ifaceStaticMethod
? "" : "non-",
2028 iface
->m_preClass
->name()->data(), methName
->data(),
2029 classStaticMethod
? "" : "non-",
2030 m_preClass
->name()->data());
2032 if ((imeth
->attrs() & AttrPublic
) &&
2033 !(meth
->attrs() & AttrPublic
)) {
2034 raise_error("Access level to %s::%s() must be public "
2035 "(as in interface %s)", m_preClass
->name()->data(),
2036 methName
->data(), iface
->m_preClass
->name()->data());
2038 meth
->parametersCompat(m_preClass
.get(), imeth
);
2043 void Class::setInterfaces() {
2044 InterfaceMap::Builder interfacesBuilder
;
2045 if (m_parent
.get() != nullptr) {
2046 int size
= m_parent
->m_interfaces
.size();
2047 for (int i
= 0; i
< size
; i
++) {
2048 Class
* interface
= m_parent
->m_interfaces
[i
];
2049 interfacesBuilder
.add(interface
->name(), interface
);
2052 for (PreClass::InterfaceVec::const_iterator it
=
2053 m_preClass
->interfaces().begin();
2054 it
!= m_preClass
->interfaces().end(); ++it
) {
2055 Class
* cp
= Unit::loadClass(*it
);
2056 if (cp
== nullptr) {
2057 raise_error("Undefined interface: %s", (*it
)->data());
2059 if (!(cp
->attrs() & AttrInterface
)) {
2060 raise_error("%s cannot implement %s - it is not an interface",
2061 m_preClass
->name()->data(), cp
->name()->data());
2063 m_declInterfaces
.push_back(ClassPtr(cp
));
2064 if (interfacesBuilder
.find(cp
->name()) == interfacesBuilder
.end()) {
2065 interfacesBuilder
.add(cp
->name(), cp
);
2067 int size
= cp
->m_interfaces
.size();
2068 for (int i
= 0; i
< size
; i
++) {
2069 Class
* interface
= cp
->m_interfaces
[i
];
2070 interfacesBuilder
.find(interface
->name());
2071 if (interfacesBuilder
.find(interface
->name()) ==
2072 interfacesBuilder
.end()) {
2073 interfacesBuilder
.add(interface
->name(), interface
);
2077 m_interfaces
.create(interfacesBuilder
);
2078 checkInterfaceMethods();
2081 void Class::setUsedTraits() {
2082 for (PreClass::UsedTraitVec::const_iterator
2083 it
= m_preClass
->usedTraits().begin();
2084 it
!= m_preClass
->usedTraits().end(); it
++) {
2085 Class
* classPtr
= Unit::loadClass(*it
);
2086 if (classPtr
== nullptr) {
2087 raise_error("Trait '%s' not found", (*it
)->data());
2089 if (!(classPtr
->attrs() & AttrTrait
)) {
2090 raise_error("%s cannot use %s - it is not a trait",
2091 m_preClass
->name()->data(),
2092 classPtr
->name()->data());
2094 m_usedTraits
.push_back(ClassPtr(classPtr
));
2098 void Class::setClassVec() {
2099 if (m_classVecLen
> 1) {
2100 assert(m_parent
.get() != nullptr);
2101 memcpy(m_classVec
, m_parent
->m_classVec
,
2102 (m_classVecLen
-1) * sizeof(Class
*));
2104 m_classVec
[m_classVecLen
-1] = this;
2107 void Class::setInstanceBits() {
2108 setInstanceBitsImpl
<false>();
2110 void Class::setInstanceBitsAndParents() {
2111 setInstanceBitsImpl
<true>();
2114 template<bool setParents
>
2115 void Class::setInstanceBitsImpl() {
2116 // Bit 0 is reserved to indicate whether or not the rest of the bits
2117 // are initialized yet.
2118 if (m_instanceBits
.test(0)) return;
2122 auto setBits
= [&](Class
* c
) {
2123 if (setParents
) c
->setInstanceBitsAndParents();
2124 bits
|= c
->m_instanceBits
;
2126 if (m_parent
.get()) setBits(m_parent
.get());
2127 for (auto& di
: m_declInterfaces
) setBits(di
.get());
2130 if (mapGet(s_instanceBits
, m_preClass
->name(), &bit
)) {
2133 m_instanceBits
= bits
;
2136 // Finds the base class defining the given method (NULL if none).
2137 // Note: for methods imported via traits, the base class is the one that
2138 // uses/imports the trait.
2139 Class
* Class::findMethodBaseClass(const StringData
* methName
) {
2140 const Func
* f
= lookupMethod(methName
);
2141 if (f
== nullptr) return nullptr;
2142 return f
->baseCls();
2145 void Class::getMethodNames(const Class
* ctx
, HphpArray
* methods
) const {
2146 Func
* const* pcMethods
= m_preClass
->methods();
2147 for (size_t i
= 0, sz
= m_preClass
->numMethods(); i
< sz
; i
++) {
2148 Func
* func
= pcMethods
[i
];
2149 if (func
->isGenerated()) continue;
2150 if (!(func
->attrs() & AttrPublic
)) {
2153 if (func
->attrs() & AttrPrivate
) continue;
2154 func
= lookupMethod(func
->name());
2155 if (!ctx
->classof(func
->baseCls()) &&
2156 !func
->baseCls()->classof(ctx
)) {
2161 methods
->set(const_cast<StringData
*>(func
->name()), true_varNR
, false);
2163 if (m_parent
.get()) m_parent
->getMethodNames(ctx
, methods
);
2164 for (int i
= 0, sz
= m_declInterfaces
.size(); i
< sz
; i
++) {
2165 m_declInterfaces
[i
]->getMethodNames(ctx
, methods
);
2169 // Returns true iff this class declared the given method.
2170 // For trait methods, the class declaring them is the one that uses/imports
2172 bool Class::declaredMethod(const Func
* method
) {
2173 if (method
->preClass()->attrs() & AttrTrait
) {
2174 return findMethodBaseClass(method
->name()) == this;
2176 return method
->preClass() == m_preClass
.get();
2179 void Class::getClassInfo(ClassInfoVM
* ci
) {
2183 Attr clsAttrs
= attrs();
2185 if (clsAttrs
& AttrInterface
) attr
|= ClassInfo::IsInterface
;
2186 if (clsAttrs
& AttrAbstract
) attr
|= ClassInfo::IsAbstract
;
2187 if (clsAttrs
& AttrFinal
) attr
|= ClassInfo::IsFinal
;
2188 if (clsAttrs
& AttrTrait
) attr
|= ClassInfo::IsTrait
;
2189 if (attr
== 0) attr
= ClassInfo::IsNothing
;
2190 ci
->m_attribute
= (ClassInfo::Attribute
)attr
;
2192 ci
->m_name
= m_preClass
->name()->data();
2194 ci
->m_file
= m_preClass
->unit()->filepath()->data();
2195 ci
->m_line1
= m_preClass
->line1();
2196 ci
->m_line2
= m_preClass
->line2();
2197 ci
->m_docComment
= (m_preClass
->docComment() != nullptr)
2198 ? m_preClass
->docComment()->data() : "";
2201 if (m_parent
.get()) {
2202 ci
->m_parentClass
= m_parent
->name()->data();
2204 ci
->m_parentClass
= "";
2208 for (unsigned i
= 0; i
< m_declInterfaces
.size(); i
++) {
2209 ci
->m_interfacesVec
.push_back(
2210 m_declInterfaces
[i
]->name()->data());
2211 ci
->m_interfaces
.insert(
2212 m_declInterfaces
[i
]->name()->data());
2216 for (unsigned t
= 0; t
< m_usedTraits
.size(); t
++) {
2217 const char* traitName
= m_usedTraits
[t
]->name()->data();
2218 ci
->m_traitsVec
.push_back(traitName
);
2219 ci
->m_traits
.insert(traitName
);
2223 for (unsigned a
= 0; a
< m_traitAliases
.size(); a
++) {
2224 ci
->m_traitAliasesVec
.push_back(std::pair
<String
, String
>
2225 (m_traitAliases
[a
].first
->data(),
2226 m_traitAliases
[a
].second
->data()));
2229 #define SET_FUNCINFO_BODY \
2230 ClassInfo::MethodInfo *m = new ClassInfo::MethodInfo; \
2231 func->getFuncInfo(m); \
2232 ci->m_methods[func->name()->data()] = m; \
2233 ci->m_methodsVec.push_back(m);
2235 // Methods: in source order (from our PreClass), then traits.
2236 for (size_t i
= 0; i
< m_preClass
->numMethods(); ++i
) {
2237 Func
* func
= lookupMethod(m_preClass
->methods()[i
]->name());
2238 // Filter out special methods
2240 DEBUG_ONLY
const StringData
* name
= m_preClass
->methods()[i
]->name();
2241 assert(!strcmp(name
->data(), "86ctor"));
2244 if (func
->isGenerated()) continue;
2246 assert(declaredMethod(func
));
2250 for (Slot i
= m_traitsBeginIdx
; i
< m_traitsEndIdx
; ++i
) {
2251 Func
* func
= m_methods
[i
];
2253 if (!func
->isGenerated()) {
2257 #undef SET_FUNCINFO_BODY
2260 for (Slot i
= 0; i
< m_declProperties
.size(); ++i
) {
2261 if (m_declProperties
[i
].m_class
!= this) continue;
2262 ClassInfo::PropertyInfo
*pi
= new ClassInfo::PropertyInfo
;
2264 pi
->name
= m_declProperties
[i
].m_name
->data();
2265 Attr propAttrs
= m_declProperties
[i
].m_attrs
;
2267 if (propAttrs
& AttrProtected
) attr
|= ClassInfo::IsProtected
;
2268 if (propAttrs
& AttrPrivate
) attr
|= ClassInfo::IsPrivate
;
2269 if (attr
== 0) attr
|= ClassInfo::IsPublic
;
2270 if (propAttrs
& AttrStatic
) attr
|= ClassInfo::IsStatic
;
2271 pi
->attribute
= (ClassInfo::Attribute
)attr
;
2272 pi
->docComment
= (m_declProperties
[i
].m_docComment
!= nullptr)
2273 ? m_declProperties
[i
].m_docComment
->data() : "";
2275 ci
->m_properties
[pi
->name
] = pi
;
2276 ci
->m_propertiesVec
.push_back(pi
);
2279 for (Slot i
= 0; i
< m_staticProperties
.size(); ++i
) {
2280 if (m_staticProperties
[i
].m_class
!= this) continue;
2281 ClassInfo::PropertyInfo
*pi
= new ClassInfo::PropertyInfo
;
2283 pi
->name
= m_staticProperties
[i
].m_name
->data();
2284 Attr propAttrs
= m_staticProperties
[i
].m_attrs
;
2286 if (propAttrs
& AttrProtected
) attr
|= ClassInfo::IsProtected
;
2287 if (propAttrs
& AttrPrivate
) attr
|= ClassInfo::IsPrivate
;
2288 if (attr
== 0) attr
|= ClassInfo::IsPublic
;
2289 if (propAttrs
& AttrStatic
) attr
|= ClassInfo::IsStatic
;
2290 pi
->attribute
= (ClassInfo::Attribute
)attr
;
2291 pi
->docComment
= (m_staticProperties
[i
].m_docComment
!= nullptr)
2292 ? m_staticProperties
[i
].m_docComment
->data() : "";
2294 ci
->m_properties
[pi
->name
] = pi
;
2295 ci
->m_propertiesVec
.push_back(pi
);
2299 for (Slot i
= 0; i
< m_constants
.size(); ++i
) {
2300 // Only include constants declared on this class
2301 if (m_constants
[i
].m_class
!= this) continue;
2303 ClassInfo::ConstantInfo
*ki
= new ClassInfo::ConstantInfo
;
2304 ki
->name
= m_constants
[i
].m_name
->data();
2305 ki
->valueLen
= m_constants
[i
].m_phpCode
->size();
2306 ki
->valueText
= m_constants
[i
].m_phpCode
->data();
2307 ki
->setValue(tvAsCVarRef(clsCnsGet(m_constants
[i
].m_name
)));
2309 ci
->m_constants
[ki
->name
] = ki
;
2310 ci
->m_constantsVec
.push_back(ki
);
2314 size_t Class::declPropOffset(Slot index
) const {
2316 return sizeof(ObjectData
) + m_builtinPropSize
2317 + index
* sizeof(TypedValue
);
2320 Class::PropInitVec::~PropInitVec() {
2321 if (!m_smart
) free(m_data
);
2324 Class::PropInitVec::PropInitVec() : m_data(nullptr), m_size(0), m_smart(false) {}
2327 Class::PropInitVec::allocInRequestArena(const PropInitVec
& src
) {
2328 ThreadInfo
* info UNUSED
= ThreadInfo::s_threadInfo
.getNoCheck();
2329 PropInitVec
* p
= new (request_arena()) PropInitVec
;
2330 p
->m_size
= src
.size();
2331 p
->m_data
= new (request_arena()) TypedValueAux
[src
.size()];
2332 memcpy(p
->m_data
, src
.m_data
, src
.size() * sizeof(*p
->m_data
));
2337 const Class::PropInitVec
&
2338 Class::PropInitVec::operator=(const PropInitVec
& piv
) {
2341 unsigned sz
= m_size
= piv
.size();
2342 if (sz
) sz
= Util::roundUpToPowerOfTwo(sz
);
2344 m_data
= (TypedValueAux
*)malloc(sz
* sizeof(*m_data
));
2346 memcpy(m_data
, piv
.m_data
, piv
.size() * sizeof(*m_data
));
2351 void Class::PropInitVec::push_back(const TypedValue
& v
) {
2354 * the allocated size is always the next power of two (or zero)
2355 * so we just need to reallocate when we hit a power of two
2357 if (!m_size
|| Util::isPowerOfTwo(m_size
)) {
2358 unsigned size
= m_size
? m_size
* 2 : 1;
2359 m_data
= (TypedValueAux
*)realloc(m_data
, size
* sizeof(*m_data
));
2362 tvDup(v
, m_data
[m_size
++]);
2365 using Transl::TargetCache::handleToRef
;
2367 const Class::PropInitVec
* Class::getPropData() const {
2368 if (m_propDataCache
== (unsigned)-1) return nullptr;
2369 return handleToRef
<PropInitVec
*>(m_propDataCache
);
2372 void Class::initPropHandle() const {
2373 if (UNLIKELY(m_propDataCache
== (unsigned)-1)) {
2374 const_cast<unsigned&>(m_propDataCache
) =
2375 Transl::TargetCache::allocClassInitProp(name());
2379 void Class::initProps() const {
2380 setPropData(initPropsImpl());
2383 void Class::setPropData(PropInitVec
* propData
) const {
2384 assert(getPropData() == nullptr);
2386 handleToRef
<PropInitVec
*>(m_propDataCache
) = propData
;
2389 TypedValue
* Class::getSPropData() const {
2390 if (m_propSDataCache
== (unsigned)-1) return nullptr;
2391 return handleToRef
<TypedValue
*>(m_propSDataCache
);
2394 void Class::initSPropHandle() const {
2395 if (UNLIKELY(m_propSDataCache
== (unsigned)-1)) {
2396 const_cast<unsigned&>(m_propSDataCache
) =
2397 Transl::TargetCache::allocClassInitSProp(name());
2401 TypedValue
* Class::initSProps() const {
2402 TypedValue
* sprops
= initSPropsImpl();
2403 setSPropData(sprops
);
2407 void Class::setSPropData(TypedValue
* sPropData
) const {
2408 assert(getSPropData() == nullptr);
2410 handleToRef
<TypedValue
*>(m_propSDataCache
) = sPropData
;
2413 void Class::getChildren(std::vector
<TypedValue
*> &out
) {
2414 for (Slot i
= 0; i
< m_staticProperties
.size(); ++i
) {
2415 if (m_staticProperties
[i
].m_class
!= this) continue;
2416 out
.push_back(&m_staticProperties
[i
].m_val
);