Clean up VectorEffects::init
[hiphop-php.git] / hphp / runtime / vm / class.cpp
blob17a678022baaf9ce3560328ad180c7d86508fc31
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
34 #include <iostream>
35 #include <algorithm>
37 namespace HPHP {
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,
53 Attr attrs) {
54 switch (attrs & (AttrPublic|AttrProtected|AttrPrivate)) {
55 case AttrPublic: {
56 return propName;
58 case AttrProtected: {
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);
66 case AttrPrivate: {
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 //=============================================================================
79 // PreClass::Prop.
81 PreClass::Prop::Prop(PreClass* preClass,
82 const StringData* n,
83 Attr attrs,
84 const StringData* typeConstraint,
85 const StringData* docComment,
86 const TypedValue& val,
87 DataType hphpcType)
88 : m_preClass(preClass)
89 , m_name(n)
90 , m_attrs(attrs)
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 {
100 out << "Property ";
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>";
108 } else {
109 std::stringstream ss;
110 staticStreamer(&m_val, ss);
111 out << ss.str();
113 out << std::endl;
116 //=============================================================================
117 // PreClass::Const.
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),
123 m_phpCode(phpCode) {
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()
129 << " = ";
130 if (m_val.m_type == KindOfUninit) {
131 out << "<non-scalar>";
132 } else {
133 std::stringstream ss;
134 staticStreamer(&m_val, ss);
135 out << ss.str();
137 out << std::endl;
140 //=============================================================================
141 // PreClass.
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() {
158 delete this;
161 void PreClass::prettyPrint(std::ostream &out) const {
162 out << "Class ";
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)";
172 if (m_id != -1) {
173 out << " (ID " << m_id << ")";
175 out << std::endl;
177 for (Func* const* it = methods(); it != methods() + numMethods(); ++it) {
178 out << " ";
179 (*it)->prettyPrint(out);
181 for (const Prop* it = properties();
182 it != properties() + numProperties();
183 ++it) {
184 out << " ";
185 it->prettyPrint(out);
187 for (const Const* it = constants();
188 it != constants() + numConstants();
189 ++it) {
190 out << " ";
191 it->prettyPrint(out);
195 //=============================================================================
196 // Class.
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));
201 try {
202 return new (mem) Class(preClass, parent, classVecLen);
203 } catch (...) {
204 Util::low_free(mem);
205 throw;
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) {
215 setParent();
216 setUsedTraits();
217 setMethods();
218 setSpecial();
219 setODAttributes();
220 setInterfaces();
221 setConstants();
222 setProperties();
223 setInitializers();
224 setClassVec();
227 Class::~Class() {
228 releaseRefs();
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())) {
260 meth = nullptr;
261 okToReleaseParent = false;
265 if (okToReleaseParent) {
266 m_parent.reset();
268 m_declInterfaces.clear();
269 m_usedTraits.clear();
272 namespace {
274 class FreeClassTrigger : public Treadmill::WorkItem {
275 TRACE_SET_MOD(treadmill);
276 Class* m_cls;
277 public:
278 explicit FreeClassTrigger(Class* cls) : m_cls(cls) {
279 TRACE(3, "FreeClassTrigger @ %p, cls %p\n", this, m_cls);
281 void operator()() {
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.
302 m_cachedOffset = 0;
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
311 * could call destroy
313 releaseRefs();
314 Treadmill::WorkItem::enqueue(new FreeClassTrigger(this));
317 void Class::atomicRelease() {
318 assert(!m_cachedOffset);
319 assert(!getCount());
320 this->~Class();
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)) {
336 return false;
338 for (auto const& declInterface : m_declInterfaces) {
339 if (!Transl::TargetCache::isPersistentHandle(
340 declInterface->m_cachedOffset)) {
341 return false;
344 for (auto const& usedTrait : m_usedTraits) {
345 if (!Transl::TargetCache::isPersistentHandle(
346 usedTrait->m_cachedOffset)) {
347 return false;
350 return true;
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;
367 uint64_t total = 0;
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
386 unsigned i = 1;
387 uint64_t accum = 0;
388 for (auto& item : counts) {
389 if (i >= kInstanceBits) break;
390 if (Class* cls = Unit::lookupUniqueClass(item.first)) {
391 if (!(cls->attrs() & AttrUnique)) {
392 continue;
395 s_instanceBits[item.first] = i;
396 accum += item.second;
397 ++i;
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"
403 " checks\n",
404 __FUNCTION__, i-1, accum, 100.0 * accum / total);
405 if (Trace::moduleEnabledRelease(Trace::instancebits, 2)) {
406 accum = 0;
407 i = 1;
408 for (auto& pair : counts) {
409 if (i >= 256) {
410 Trace::traceRelease("skipping the remainder of the %zu classes\n",
411 counts.size());
412 break;
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,
418 pair.first->data());
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());
438 unsigned inc = 1;
439 Class* c = Unit::lookupClass(name);
440 if (c && (c->attrs() & AttrInterface)) {
441 // Favor interfaces
442 inc = 250;
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))) {
450 acc->second += 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;
465 unsigned 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);
470 return true;
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
490 * correctly.
492 Class::Avail Class::avail(Class*& parent, bool tryAutoload /*=false*/) const {
493 if (Class *ourParent = m_parent.get()) {
494 if (!parent) {
495 PreClass *ppcls = ourParent->m_preClass.get();
496 parent = Unit::getClass(ppcls->namedEntity(), ppcls->name(), tryAutoload);
497 if (!parent) {
498 parent = ourParent;
499 return Avail::Fail;
502 if (parent != ourParent) {
503 if (UNLIKELY(ourParent->isZombie())) {
504 const_cast<Class*>(this)->destroy();
506 return Avail::False;
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(),
513 tryAutoload);
514 if (interface != declInterface) {
515 if (interface == nullptr) {
516 parent = declInterface;
517 return Avail::Fail;
519 if (UNLIKELY(declInterface->isZombie())) {
520 const_cast<Class*>(this)->destroy();
522 return Avail::False;
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(),
529 tryAutoload);
530 if (trait != usedTrait) {
531 if (trait == nullptr) {
532 parent = usedTrait;
533 return Avail::Fail;
535 if (UNLIKELY(usedTrait->isZombie())) {
536 const_cast<Class*>(this)->destroy();
538 return Avail::False;
541 return Avail::True;
544 void Class::initialize(TypedValue*& sProps) const {
545 if (m_pinitVec.size() > 0) {
546 if (getPropData() == nullptr) {
547 initProps();
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();
558 } else {
559 sProps = nullptr;
563 void Class::initialize() const {
564 TypedValue* sProps;
565 initialize(sProps);
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
575 // the new propVec.
576 request_arena().beginFrame();
577 PropInitVec* propVec = PropInitVec::allocInRequestArena(m_declPropInit);
578 size_t nProps = numDeclProperties();
581 Array args;
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();
592 TypedValue tv;
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);
612 } else {
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);
621 try {
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) {
626 TypedValue retval;
627 g_vmContext->invokeFunc(&retval, *it, args, nullptr,
628 const_cast<Class*>(this));
629 assert(retval.m_type == KindOfNull);
631 } catch (...) {
632 // Undo the allocation of propVec
633 request_arena().endFrame();
634 throw;
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);
646 assert(value);
647 tvDup(*value, prop);
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.
658 Slot slot = 0;
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;
665 } else {
666 tvAsVariant(tv).setEvalScalar();
667 tv->deepInit() = false;
671 return propVec;
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;
684 assert(baseClass);
685 // If ctx == baseClass, we know we have the right property
686 // and we can stop here.
687 if (ctx == baseClass) {
688 accessible = true;
689 return propInd;
691 // The anonymous context cannot access protected or private
692 // properties, so we can fail fast here.
693 if (ctx == nullptr) {
694 accessible = false;
695 return propInd;
697 assert(ctx);
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.
702 accessible = false;
703 } else {
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.
708 accessible = true;
709 return propInd;
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
716 // fail fast here.
717 accessible = false;
718 return propInd;
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
722 // name.
723 accessible = true;
724 assert(baseClass->classof(ctx));
726 } else {
727 // The property is public (or we're in the debugger and we are bypassing
728 // accessibility checks).
729 accessible = true;
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.
732 if (ctx == this) {
733 return propInd;
735 // We still need to check if ctx defines a private property with the
736 // same name.
738 } else {
739 // We didn't find a visible declared property in this's property map
740 accessible = false;
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
750 // have found.
751 accessible = true;
752 return ctxPropInd;
755 return propInd;
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
762 // properties.
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];
784 } else {
785 // Alias parent class's static property.
786 bool visible, accessible;
787 storage = sProp.m_class->getSProp(nullptr, sProp.m_name, visible,
788 accessible);
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);
805 args->incRefCount();
806 try {
808 Variant arg0(&nvtWrapper);
809 args = args->appendRef(arg0, false);
811 for (unsigned i = 0; i < m_sinitVec.size(); i++) {
812 TypedValue retval;
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);
821 args->release();
822 assert(nvtWrapper.getCount() == 1);
823 } catch (...) {
824 assert(args->getCount() == 1);
825 args->release();
826 assert(nvtWrapper.getCount() == 1);
827 throw;
831 return spropTable;
834 TypedValue* Class::getSProp(Class* ctx, const StringData* sPropName,
835 bool& visible, bool& accessible) const {
836 TypedValue* sProps;
837 initialize(sProps);
839 Slot sPropInd = lookupSProp(sPropName);
840 if (sPropInd == kInvalidSlot) {
841 // Non-existant property.
842 visible = false;
843 accessible = false;
844 return nullptr;
847 visible = true;
848 if (ctx == this) {
849 // Property access is from within a method of this class, so the property
850 // is accessible.
851 accessible = true;
852 } else {
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)) {
858 case AttrPublic:
859 case AttrProtected: accessible = true; break;
860 case AttrPrivate:
861 accessible = g_vmContext->getDebuggerBypassCheck(); break;
862 default: not_reached();
864 } else {
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;
869 case AttrProtected:
870 case AttrPrivate:
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");
881 return sProp;
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);
925 return constants;
928 TypedValue* Class::cnsNameToTV(const StringData* clsCnsName,
929 Slot& clsCnsInd) const {
930 clsCnsInd = m_constants.findIndex(clsCnsName);
931 if (clsCnsInd == kInvalidSlot) {
932 return nullptr;
934 return const_cast<TypedValue*>(&m_constants[clsCnsInd].m_val);
937 TypedValue* Class::clsCnsGet(const StringData* clsCnsName) const {
938 Slot clsCnsInd;
939 TypedValue* clsCns = cnsNameToTV(clsCnsName, clsCnsInd);
940 if (!clsCns || clsCns->m_type != KindOfUninit) {
941 return clsCns;
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);
957 TypedValue tv[1];
958 tv->m_data.pstr = (StringData*)clsCnsName;
959 tv->m_type = KindOfString;
960 clsCnsName->incRefCount();
961 g_vmContext->invokeFuncFew(clsCns, meth86cinit, ActRec::encodeClass(this),
962 nullptr, 1, tv);
964 return clsCns;
967 DataType Class::clsCnsType(const StringData* cnsName) const {
968 Slot slot;
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;
973 return cns->m_type;
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);
1010 f = f->clone();
1011 f->setNewFuncId();
1012 f->setCls(cls);
1013 f->setBaseCls(cls);
1014 f->setHasPrivateAncestor(false);
1015 return f;
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;
1033 return;
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;
1047 return;
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;
1055 return;
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;
1086 } else {
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
1109 return nullptr;
1111 traitCls = t.get();
1114 return traitCls;
1117 void Class::setImportTraitMethodModifiers(TraitMethodList& methList,
1118 Class* traitCls,
1119 Attr modifiers) {
1120 for (TraitMethodList::iterator iter = methList.begin();
1121 iter != methList.end(); iter++) {
1122 if (iter->m_trait == traitCls) {
1123 iter->m_modifiers = modifiers;
1124 return;
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);
1151 } else {
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);
1163 if (!traitMeth) {
1164 raise_error("unknown trait method '%s'", origMethName->data());
1167 Attr ruleModifiers;
1168 if (origMethName == newMethName) {
1169 ruleModifiers = rule.getModifiers();
1170 setImportTraitMethodModifiers(importMethToTraitMap[origMethName],
1171 traitCls, ruleModifiers);
1172 } else {
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()) {
1204 return;
1207 if (modifiers == AttrNone) {
1208 modifiers = method->attrs();
1209 } else {
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
1223 // implementation
1224 return;
1226 parentMethod = existingMethod;
1228 Func* f = method->clone();
1229 f->setNewFuncId();
1230 f->setClsAndName(this, methName);
1231 f->setAttrs(modifiers);
1232 if (!parentMethod) {
1233 // New method
1234 builder.add(methName, f);
1235 f->setBaseCls(this);
1236 f->setHasPrivateAncestor(false);
1237 } else {
1238 // Override an existing method
1239 Class* baseClass;
1241 methodOverrideCheck(parentMethod, f);
1243 assert(!(f->attrs() & AttrPrivate) ||
1244 (parentMethod->attrs() & AttrPrivate));
1245 if ((parentMethod->attrs() & AttrPrivate) || (f->attrs() & AttrPrivate)) {
1246 baseClass = this;
1247 } else {
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
1260 // 2) duplicate
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;
1274 } else {
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;
1295 // fatals on error
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) {
1326 continue;
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 "
1360 "class %s",
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))) {
1368 raise_error(
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];
1400 assert(f);
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
1407 // overriden below.
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
1417 // parent.
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.
1428 continue;
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);
1437 // Overlay.
1438 Func* f = method->clone();
1439 f->setNewFuncId();
1440 f->setCls(this);
1441 Class* baseClass;
1442 assert(!(f->attrs() & AttrPrivate) ||
1443 (parentMethod->attrs() & AttrPrivate));
1444 if ((parentMethod->attrs() & AttrPrivate) || (f->attrs() & AttrPrivate)) {
1445 baseClass = this;
1446 } else {
1447 baseClass = parentMethod->baseCls();
1449 f->setBaseCls(baseClass);
1450 f->setHasPrivateAncestor(
1451 parentMethod->hasPrivateAncestor() ||
1452 (parentMethod->attrs() & AttrPrivate));
1453 builder[it2->second] = f;
1454 } else {
1455 // This is the first class that declares the method
1456 Class* baseClass = this;
1457 // Append.
1458 Func* f = method->clone();
1459 f->setNewFuncId();
1460 f->setCls(this);
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
1474 // static locals
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
1483 // class.
1484 f = f->clone();
1485 if (f->attrs() & AttrClone) {
1486 f->setCls(this);
1488 f->setNewFuncId();
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");
1521 m_ODAttrs = 0;
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();
1567 } else {
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());
1573 } else {
1574 // Append constant.
1575 Const constant;
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);
1591 } else {
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.
1616 Prop prop;
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);
1627 } else {
1628 ++numInaccessible;
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.
1638 SProp sProp;
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.
1674 if (parentProp
1675 && (preProp->attrs() & (AttrPublic|AttrProtected|AttrPrivate))
1676 > (parentProp->m_attrs & (AttrPublic|AttrProtected|AttrPrivate))) {
1677 raise_error(
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)) {
1687 case AttrPrivate: {
1688 // Append a new private property.
1689 Prop prop;
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())
1701 ->val());
1702 break;
1704 case AttrProtected: {
1705 // Check whether a superclass has already declared this protected
1706 // property.
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]);
1716 break;
1718 // Append a new protected property.
1719 Prop prop;
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())
1731 ->val());
1732 break;
1734 case AttrPublic: {
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))
1741 == AttrProtected) {
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]);
1753 break;
1755 // Append a new public property.
1756 Prop prop;
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())
1768 ->val());
1769 break;
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.
1778 Class* ancestor;
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))) {
1797 raise_error(
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) {
1807 SProp sProp;
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;
1834 case KindOfBoolean:
1835 case KindOfInt64:
1836 case KindOfDouble:
1837 case KindOfStaticString:
1838 case KindOfString:
1839 return same(tvAsVariant(&tv1), tvAsVariant(&tv2));
1840 default: return false;
1844 void Class::importTraitInstanceProp(Class* trait,
1845 Prop& traitProp,
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(),
1857 prop.m_name,
1858 prop.m_attrs);
1860 curPropMap.add(prop.m_name, prop);
1861 m_declPropInit.push_back(traitPropVal);
1862 } else {
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,
1875 SProp& traitProp,
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);
1890 } else {
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;
1898 } else {
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,
1925 curPropMap);
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,
1932 curSPropMap);
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
1958 // initialization.
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);
1972 if (sinit) {
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.
2011 continue;
2013 } else {
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(),
2019 methName->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 "
2026 "in class %s",
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;
2120 InstanceBits bits;
2121 bits.set(0);
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());
2129 unsigned bit;
2130 if (mapGet(s_instanceBits, m_preClass->name(), &bit)) {
2131 bits.set(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)) {
2151 if (!ctx) continue;
2152 if (ctx != this) {
2153 if (func->attrs() & AttrPrivate) continue;
2154 func = lookupMethod(func->name());
2155 if (!ctx->classof(func->baseCls()) &&
2156 !func->baseCls()->classof(ctx)) {
2157 continue;
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
2171 // the trait.
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) {
2180 assert(ci);
2182 // Miscellaneous.
2183 Attr clsAttrs = attrs();
2184 int attr = 0;
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() : "";
2200 // Parent class.
2201 if (m_parent.get()) {
2202 ci->m_parentClass = m_parent->name()->data();
2203 } else {
2204 ci->m_parentClass = "";
2207 // Interfaces.
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());
2215 // Used traits.
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);
2222 // Trait aliases.
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
2239 if (!func) {
2240 DEBUG_ONLY const StringData* name = m_preClass->methods()[i]->name();
2241 assert(!strcmp(name->data(), "86ctor"));
2242 continue;
2244 if (func->isGenerated()) continue;
2245 assert(func);
2246 assert(declaredMethod(func));
2247 SET_FUNCINFO_BODY;
2250 for (Slot i = m_traitsBeginIdx; i < m_traitsEndIdx; ++i) {
2251 Func* func = m_methods[i];
2252 assert(func);
2253 if (!func->isGenerated()) {
2254 SET_FUNCINFO_BODY;
2257 #undef SET_FUNCINFO_BODY
2259 // Properties.
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;
2263 pi->owner = ci;
2264 pi->name = m_declProperties[i].m_name->data();
2265 Attr propAttrs = m_declProperties[i].m_attrs;
2266 attr = 0;
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;
2282 pi->owner = ci;
2283 pi->name = m_staticProperties[i].m_name->data();
2284 Attr propAttrs = m_staticProperties[i].m_attrs;
2285 attr = 0;
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);
2298 // Constants.
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 {
2315 assert(index >= 0);
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) {}
2326 Class::PropInitVec*
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));
2333 p->m_smart = true;
2334 return p;
2337 const Class::PropInitVec&
2338 Class::PropInitVec::operator=(const PropInitVec& piv) {
2339 assert(!m_smart);
2340 if (this != &piv) {
2341 unsigned sz = m_size = piv.size();
2342 if (sz) sz = Util::roundUpToPowerOfTwo(sz);
2343 free(m_data);
2344 m_data = (TypedValueAux*)malloc(sz * sizeof(*m_data));
2345 assert(m_data);
2346 memcpy(m_data, piv.m_data, piv.size() * sizeof(*m_data));
2348 return *this;
2351 void Class::PropInitVec::push_back(const TypedValue& v) {
2352 assert(!m_smart);
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));
2360 assert(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);
2385 initPropHandle();
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);
2404 return sprops;
2407 void Class::setSPropData(TypedValue* sPropData) const {
2408 assert(getSPropData() == nullptr);
2409 initSPropHandle();
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);
2420 } // HPHP::VM