Support for InstanceOfD translations
[hiphop-php.git] / src / runtime / vm / class.cpp
blobc482aad5edb04cb015ac6b2a287c03c37d6e0bb9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include <boost/checked_delete.hpp>
18 #include <boost/optional.hpp>
19 #include <boost/utility/typed_in_place_factory.hpp>
21 #include <iostream>
22 #include <algorithm>
24 #include "runtime/base/base_includes.h"
25 #include "util/util.h"
26 #include "util/debug.h"
27 #include "runtime/vm/core_types.h"
28 #include "runtime/vm/hhbc.h"
29 #include "runtime/vm/class.h"
30 #include "runtime/vm/repo.h"
31 #include "runtime/vm/translator/targetcache.h"
32 #include "runtime/vm/translator/translator.h"
33 #include "runtime/vm/blob_helper.h"
34 #include "runtime/vm/treadmill.h"
35 #include "runtime/vm/name_value_table.h"
36 #include "runtime/vm/name_value_table_wrapper.h"
37 #include "runtime/vm/request_arena.h"
38 #include "system/lib/systemlib.h"
39 #include "util/logger.h"
41 namespace HPHP {
42 namespace VM {
44 static StringData* sd86ctor = StringData::GetStaticString("86ctor");
45 static StringData* sd86pinit = StringData::GetStaticString("86pinit");
46 static StringData* sd86sinit = StringData::GetStaticString("86sinit");
48 hphp_hash_map<const StringData*, const HhbcExtClassInfo*,
49 string_data_hash, string_data_isame> Class::s_extClassHash;
50 Class::InstanceCounts Class::s_instanceCounts;
51 ReadWriteMutex Class::s_instanceCountsLock(RankInstanceCounts);
52 Class::InstanceBitsMap Class::s_instanceBits;
53 ReadWriteMutex Class::s_instanceBitsLock(RankInstanceBits);
54 std::atomic<bool> Class::s_instanceBitsInit{false};
56 static const StringData* manglePropName(const StringData* className,
57 const StringData* propName,
58 Attr attrs) {
59 switch (attrs & (AttrPublic|AttrProtected|AttrPrivate)) {
60 case AttrPublic: {
61 return propName;
63 case AttrProtected: {
64 std::string mangledName = "";
65 mangledName.push_back('\0');
66 mangledName.push_back('*');
67 mangledName.push_back('\0');
68 mangledName += propName->data();
69 return StringData::GetStaticString(mangledName);
71 case AttrPrivate: {
72 std::string mangledName = "";
73 mangledName.push_back('\0');
74 mangledName += className->data();
75 mangledName.push_back('\0');
76 mangledName += propName->data();
77 return StringData::GetStaticString(mangledName);
79 default: not_reached();
83 //=============================================================================
84 // PreClass::Prop.
86 PreClass::Prop::Prop(PreClass* preClass, const StringData* n, Attr attrs,
87 const StringData* docComment, const TypedValue& val)
88 : m_preClass(preClass), m_name(n), m_attrs(attrs),
89 m_docComment(docComment) {
90 m_mangledName = manglePropName(preClass->name(), n, attrs);
91 memcpy(&m_val, &val, sizeof(TypedValue));
94 void PreClass::Prop::prettyPrint(std::ostream& out) const {
95 out << "Property ";
96 if (m_attrs & AttrStatic) { out << "static "; }
97 if (m_attrs & AttrPublic) { out << "public "; }
98 if (m_attrs & AttrProtected) { out << "protected "; }
99 if (m_attrs & AttrPrivate) { out << "private "; }
100 out << m_preClass->name()->data() << "::" << m_name->data() << " = ";
101 if (m_val.m_type == KindOfUninit) {
102 out << "<non-scalar>";
103 } else {
104 std::stringstream ss;
105 staticStreamer(&m_val, ss);
106 out << ss.str();
108 out << std::endl;
111 //=============================================================================
112 // PreClass::Const.
114 PreClass::Const::Const(PreClass* preClass, const StringData* n,
115 const TypedValue& val, const StringData* phpCode)
116 : m_preClass(preClass), m_name(n), m_phpCode(phpCode) {
117 memcpy(&m_val, &val, sizeof(TypedValue));
120 void PreClass::Const::prettyPrint(std::ostream& out) const {
121 out << "Constant " << m_preClass->name()->data() << "::" << m_name->data()
122 << " = ";
123 if (m_val.m_type == KindOfUninit) {
124 out << "<non-scalar>";
125 } else {
126 std::stringstream ss;
127 staticStreamer(&m_val, ss);
128 out << ss.str();
130 out << std::endl;
133 //=============================================================================
134 // PreClass.
136 PreClass::PreClass(Unit* unit, int line1, int line2, Offset o,
137 const StringData* n, Attr attrs, const StringData* parent,
138 const StringData* docComment, Id id, Hoistable hoistable)
139 : m_unit(unit), m_line1(line1), m_line2(line2), m_offset(o), m_id(id),
140 m_builtinPropSize(0), m_attrs(attrs), m_hoistable(hoistable),
141 m_name(n), m_parent(parent), m_docComment(docComment),
142 m_InstanceCtor(NULL) {
143 m_namedEntity = Unit::GetNamedEntity(n);
146 PreClass::~PreClass() {
147 std::for_each(methods(), methods() + numMethods(),
148 boost::checked_deleter<Func>());
151 void PreClass::atomicRelease() {
152 delete this;
155 void PreClass::prettyPrint(std::ostream &out) const {
156 out << "Class ";
157 if (m_attrs & AttrAbstract) { out << "abstract "; }
158 if (m_attrs & AttrFinal) { out << "final "; }
159 if (m_attrs & AttrInterface) { out << "interface "; }
160 out << m_name->data() << " at " << m_offset;
161 if (m_hoistable == MaybeHoistable) {
162 out << " (maybe-hoistable)";
163 } else if (m_hoistable == AlwaysHoistable) {
164 out << " (always-hoistable)";
166 if (m_id != -1) {
167 out << " (ID " << m_id << ")";
169 out << std::endl;
171 for (Func* const* it = methods(); it != methods() + numMethods(); ++it) {
172 out << " ";
173 (*it)->prettyPrint(out);
175 for (const Prop* it = properties();
176 it != properties() + numProperties();
177 ++it) {
178 out << " ";
179 it->prettyPrint(out);
181 for (const Const* it = constants();
182 it != constants() + numConstants();
183 ++it) {
184 out << " ";
185 it->prettyPrint(out);
189 //=============================================================================
190 // PreClassEmitter::Prop.
192 PreClassEmitter::Prop::Prop(const PreClassEmitter* pce, const StringData* n,
193 Attr attrs, const StringData* docComment,
194 TypedValue* val)
195 : m_name(n), m_attrs(attrs), m_docComment(docComment) {
196 m_mangledName = manglePropName(pce->name(), n, attrs);
197 memcpy(&m_val, val, sizeof(TypedValue));
200 PreClassEmitter::Prop::~Prop() {
203 //=============================================================================
204 // PreClassEmitter.
206 PreClassEmitter::PreClassEmitter(UnitEmitter& ue,
207 Id id,
208 const StringData* n,
209 PreClass::Hoistable hoistable)
210 : m_ue(ue)
211 , m_name(n)
212 , m_id(id)
213 , m_hoistable(hoistable)
214 , m_InstanceCtor(NULL)
215 , m_builtinPropSize(0)
218 void PreClassEmitter::init(int line1, int line2, Offset offset, Attr attrs,
219 const StringData* parent,
220 const StringData* docComment) {
221 m_line1 = line1;
222 m_line2 = line2;
223 m_offset = offset;
224 m_attrs = attrs;
225 m_parent = parent;
226 m_docComment = docComment;
229 PreClassEmitter::~PreClassEmitter() {
230 for (MethodVec::const_iterator it = m_methods.begin();
231 it != m_methods.end(); ++it) {
232 delete *it;
236 void PreClassEmitter::addInterface(const StringData* n) {
237 m_interfaces.push_back(n);
240 bool PreClassEmitter::addMethod(FuncEmitter* method) {
241 MethodMap::const_iterator it = m_methodMap.find(method->name());
242 if (it != m_methodMap.end()) {
243 return false;
245 m_methods.push_back(method);
246 m_methodMap[method->name()] = method;
247 return true;
250 bool PreClassEmitter::addProperty(const StringData* n, Attr attrs,
251 const StringData* docComment,
252 TypedValue* val) {
253 PropMap::Builder::const_iterator it = m_propMap.find(n);
254 if (it != m_propMap.end()) {
255 return false;
257 PreClassEmitter::Prop prop(this, n, attrs, docComment, val);
258 m_propMap.add(prop.name(), prop);
259 return true;
262 const PreClassEmitter::Prop&
263 PreClassEmitter::lookupProp(const StringData* propName) const {
264 PropMap::Builder::const_iterator it = m_propMap.find(propName);
265 assert(it != m_propMap.end());
266 Slot idx = it->second;
267 return m_propMap[idx];
270 bool PreClassEmitter::addConstant(const StringData* n, TypedValue* val,
271 const StringData* phpCode) {
272 ConstMap::Builder::const_iterator it = m_constMap.find(n);
273 if (it != m_constMap.end()) {
274 return false;
276 PreClassEmitter::Const const_(n, val, phpCode);
277 m_constMap.add(const_.name(), const_);
278 return true;
281 void PreClassEmitter::addUsedTrait(const StringData* traitName) {
282 m_usedTraits.push_back(traitName);
285 void PreClassEmitter::addTraitPrecRule(
286 const PreClass::TraitPrecRule &rule) {
287 m_traitPrecRules.push_back(rule);
290 void PreClassEmitter::addTraitAliasRule(
291 const PreClass::TraitAliasRule &rule) {
292 m_traitAliasRules.push_back(rule);
295 void PreClassEmitter::addUserAttribute(const StringData* name, TypedValue tv) {
296 m_userAttributes[name] = tv;
299 void PreClassEmitter::commit(RepoTxn& txn) const {
300 Repo& repo = Repo::get();
301 PreClassRepoProxy& pcrp = repo.pcrp();
302 int repoId = m_ue.repoId();
303 int64 usn = m_ue.sn();
304 pcrp.insertPreClass(repoId)
305 .insert(*this, txn, usn, m_id, m_name, m_hoistable);
307 for (MethodVec::const_iterator it = m_methods.begin();
308 it != m_methods.end(); ++it) {
309 (*it)->commit(txn);
313 void PreClassEmitter::setBuiltinClassInfo(const ClassInfo* info,
314 BuiltinCtorFunction ctorFunc,
315 int sz) {
316 if (info->getAttribute() & ClassInfo::IsFinal) {
317 m_attrs = m_attrs | AttrFinal;
319 if (info->getAttribute() & ClassInfo::IsAbstract) {
320 m_attrs = m_attrs | AttrAbstract;
322 if (info->getAttribute() & ClassInfo::IsTrait) {
323 m_attrs = m_attrs | AttrTrait;
325 m_attrs = m_attrs | AttrUnique;
326 m_InstanceCtor = ctorFunc;
327 m_builtinPropSize = sz - sizeof(ObjectData);
330 PreClass* PreClassEmitter::create(Unit& unit) const {
331 Attr attrs = m_attrs;
332 if (attrs & AttrPersistent &&
333 !RuntimeOption::RepoAuthoritative && SystemLib::s_inited) {
334 attrs = Attr(attrs & ~AttrPersistent);
336 PreClass* pc = new PreClass(&unit, m_line1, m_line2, m_offset, m_name,
337 attrs, m_parent, m_docComment, m_id,
338 m_hoistable);
339 pc->m_InstanceCtor = m_InstanceCtor;
340 pc->m_builtinPropSize = m_builtinPropSize;
341 pc->m_interfaces = m_interfaces;
342 pc->m_usedTraits = m_usedTraits;
343 pc->m_traitPrecRules = m_traitPrecRules;
344 pc->m_traitAliasRules = m_traitAliasRules;
345 pc->m_userAttributes = m_userAttributes;
347 PreClass::MethodMap::Builder methodBuild;
348 for (MethodVec::const_iterator it = m_methods.begin();
349 it != m_methods.end(); ++it) {
350 Func* f = (*it)->create(unit, pc);
351 methodBuild.add(f->name(), f);
353 pc->m_methods.create(methodBuild);
355 PreClass::PropMap::Builder propBuild;
356 for (unsigned i = 0; i < m_propMap.size(); ++i) {
357 const Prop& prop = m_propMap[i];
358 propBuild.add(prop.name(), PreClass::Prop(pc,
359 prop.name(),
360 prop.attrs(),
361 prop.docComment(),
362 prop.val()));
364 pc->m_properties.create(propBuild);
366 PreClass::ConstMap::Builder constBuild;
367 for (unsigned i = 0; i < m_constMap.size(); ++i) {
368 const Const& const_ = m_constMap[i];
369 constBuild.add(const_.name(), PreClass::Const(pc,
370 const_.name(),
371 const_.val(),
372 const_.phpCode()));
374 pc->m_constants.create(constBuild);
375 return pc;
378 template<class SerDe> void PreClassEmitter::serdeMetaData(SerDe& sd) {
379 // NOTE: name, hoistable, and a few other fields currently
380 // serialized outside of this.
381 sd(m_line1)
382 (m_line2)
383 (m_offset)
384 (m_attrs)
385 (m_parent)
386 (m_docComment)
388 (m_interfaces)
389 (m_usedTraits)
390 (m_traitPrecRules)
391 (m_traitAliasRules)
392 (m_userAttributes)
393 (m_propMap)
394 (m_constMap)
398 //=============================================================================
399 // PreClassRepoProxy.
401 PreClassRepoProxy::PreClassRepoProxy(Repo& repo)
402 : RepoProxy(repo)
403 #define PCRP_OP(c, o) \
404 , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral)
405 PCRP_OPS
406 #undef PCRP_OP
408 #define PCRP_OP(c, o) \
409 m_##o[RepoIdLocal] = &m_##o##Local; \
410 m_##o[RepoIdCentral] = &m_##o##Central;
411 PCRP_OPS
412 #undef PCRP_OP
415 PreClassRepoProxy::~PreClassRepoProxy() {
418 void PreClassRepoProxy::createSchema(int repoId, RepoTxn& txn) {
420 std::stringstream ssCreate;
421 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "PreClass")
422 << "(unitSn INTEGER, preClassId INTEGER, name TEXT,"
423 " hoistable INTEGER, extraData BLOB,"
424 " PRIMARY KEY (unitSn, preClassId));";
425 txn.exec(ssCreate.str());
429 void PreClassRepoProxy::InsertPreClassStmt
430 ::insert(const PreClassEmitter& pce, RepoTxn& txn,
431 int64 unitSn, Id preClassId,
432 const StringData* name,
433 PreClass::Hoistable hoistable) {
434 if (!prepared()) {
435 std::stringstream ssInsert;
436 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "PreClass")
437 << " VALUES(@unitSn, @preClassId, @name, @hoistable, "
438 "@extraData);";
439 txn.prepare(*this, ssInsert.str());
442 BlobEncoder extraBlob;
443 RepoTxnQuery query(txn, *this);
444 query.bindInt64("@unitSn", unitSn);
445 query.bindId("@preClassId", preClassId);
446 query.bindStaticString("@name", name);
447 query.bindInt("@hoistable", hoistable);
448 const_cast<PreClassEmitter&>(pce).serdeMetaData(extraBlob);
449 query.bindBlob("@extraData", extraBlob, /* static */ true);
450 query.exec();
453 void PreClassRepoProxy::GetPreClassesStmt
454 ::get(UnitEmitter& ue) {
455 RepoTxn txn(m_repo);
456 if (!prepared()) {
457 std::stringstream ssSelect;
458 ssSelect << "SELECT preClassId,name,hoistable,extraData FROM "
459 << m_repo.table(m_repoId, "PreClass")
460 << " WHERE unitSn == @unitSn ORDER BY preClassId ASC;";
461 txn.prepare(*this, ssSelect.str());
463 RepoTxnQuery query(txn, *this);
464 query.bindInt64("@unitSn", ue.sn());
465 do {
466 query.step();
467 if (query.row()) {
468 Id preClassId; /**/ query.getId(0, preClassId);
469 StringData* name; /**/ query.getStaticString(1, name);
470 int hoistable; /**/ query.getInt(2, hoistable);
471 BlobDecoder extraBlob = /**/ query.getBlob(3);
472 PreClassEmitter* pce = ue.newPreClassEmitter(
473 name, (PreClass::Hoistable)hoistable);
474 pce->serdeMetaData(extraBlob);
475 assert(pce->id() == preClassId);
477 } while (!query.done());
478 txn.commit();
481 //=============================================================================
482 // Class.
484 ClassPtr Class::newClass(PreClass* preClass, Class* parent) {
485 unsigned classVecLen = (parent != NULL) ? parent->m_classVecLen+1 : 1;
486 void* mem = Util::low_malloc(sizeForNClasses(classVecLen));
487 always_assert(IMPLIES(alwaysLowMem(), ptr_is_low_mem(mem)) &&
488 "All Classes must be allocated at 32-bit addresses");
489 try {
490 return ClassPtr(new (mem) Class(preClass, parent, classVecLen));
491 } catch (...) {
492 Util::low_free(mem);
493 throw;
497 Class::Class(PreClass* preClass, Class* parent, unsigned classVecLen)
498 : m_preClass(PreClassPtr(preClass)), m_parent(ClassPtr(parent)),
499 m_traitsBeginIdx(0), m_traitsEndIdx(0), m_clsInfo(NULL),
500 m_builtinPropSize(0), m_classVecLen(classVecLen), m_cachedOffset(0),
501 m_propDataCache(-1), m_propSDataCache(-1), m_InstanceCtor(NULL),
502 m_nextClass(NULL) {
503 setParent();
504 setUsedTraits();
505 setMethods();
506 setSpecial();
507 setODAttributes();
508 setInterfaces();
509 setConstants();
510 setProperties();
511 setInitializers();
512 setClassVec();
515 void Class::atomicRelease() {
516 if (m_cachedOffset != 0u) {
518 m_cachedOffset is initialied to 0, and is only set
519 when the Class is put on the list, so we only have to
520 remove this node if its NOT 0.
521 Since we're about to remove it, reset to 0 so we know
522 its safe to kill the node during the delayed Treadmill
523 callback.
525 m_cachedOffset = 0u;
526 PreClass* pcls = m_preClass.get();
528 Lock l(Unit::s_classesMutex);
529 Class *const*cls = pcls->namedEntity()->clsList();
530 while (*cls != this) {
531 assert(*cls);
532 cls = &(*cls)->m_nextClass;
534 *const_cast<Class**>(cls) = m_nextClass;
536 Treadmill::WorkItem::enqueue(new Treadmill::FreeClassTrigger(this));
537 return;
540 this->~Class();
541 free(this);
544 Class *Class::getCached() const {
545 return *(Class**)Transl::TargetCache::handleToPtr(m_cachedOffset);
548 void Class::setCached() {
549 *(Class**)Transl::TargetCache::handleToPtr(m_cachedOffset) = this;
552 bool Class::verifyPersistent() const {
553 if (!(attrs() & AttrPersistent)) return false;
554 if (m_parent.get() &&
555 !Transl::TargetCache::isPersistentHandle(m_parent->m_cachedOffset)) {
556 return false;
558 for (size_t i = 0, nInterfaces = m_declInterfaces.size();
559 i < nInterfaces; ++i) {
560 Class* declInterface = m_declInterfaces[i].get();
561 if (!Transl::TargetCache::isPersistentHandle(
562 declInterface->m_cachedOffset)) {
563 return false;
566 for (size_t i = 0; i < m_usedTraits.size(); i++) {
567 Class* usedTrait = m_usedTraits[i].get();
568 if (!Transl::TargetCache::isPersistentHandle(
569 usedTrait->m_cachedOffset)) {
570 return false;
573 return true;
576 void Class::initInstanceBits() {
577 assert(Transl::Translator::WriteLease().amOwner());
578 if (s_instanceBitsInit.load(std::memory_order_acquire)) return;
580 // First, grab a write lock on s_instanceCounts and grab the current set of
581 // counts as quickly as possible to minimize blocking other threads still
582 // trying to profile instance checks.
583 typedef std::pair<const StringData*, unsigned> Count;
584 std::vector<Count> counts;
585 uint64 total = 0;
587 // If you think of the read-write lock as a shared-exclusive lock instead,
588 // the fact that we're grabbing a write lock to iterate over the table
589 // makes more sense: it's safe to concurrently modify a
590 // tbb::concurrent_hash_map, but iteration is not guaranteed to be safe
591 // with concurrent insertions.
592 WriteLock l(s_instanceCountsLock);
593 for (auto& pair : s_instanceCounts) {
594 counts.push_back(pair);
595 total += pair.second;
598 std::sort(counts.begin(), counts.end(), [&](const Count& a, const Count& b) {
599 return a.second > b.second;
602 // Next, initialize s_instanceBits with the top 127 most checked classes. Bit
603 // 0 is reserved as an 'initialized' flag
604 unsigned i = 1;
605 uint64 accum = 0;
606 for (auto& item : counts) {
607 if (i >= kInstanceBits) break;
608 s_instanceBits[item.first] = i;
609 accum += item.second;
610 ++i;
613 // Print out stats about what we ended up using
614 if (Trace::moduleEnabledRelease(Trace::instancebits, 1)) {
615 Trace::traceRelease("%s: %u classes, %u (%.2f%%) of warmup checks\n",
616 __FUNCTION__, i-1, accum, 100.0 * accum / total);
617 if (Trace::moduleEnabledRelease(Trace::instancebits, 2)) {
618 accum = 0;
619 i = 1;
620 for (auto& pair : counts) {
621 if (i >= 256) {
622 Trace::traceRelease("skipping the remainder of the %llu classes\n",
623 counts.size());
624 break;
626 accum += pair.second;
627 Trace::traceRelease("%3u %5.2f%% %7u -- %6.2f%% %7u %s\n",
628 i++, 100.0 * pair.second / total, pair.second,
629 100.0 * accum / total, accum,
630 pair.first->data());
635 // Finally, update m_instanceBits on every Class that currently exists. This
636 // must be done while holding a lock that blocks insertion of new Classes
637 // into their class lists, but in practice most Classes will already be
638 // created by now and this process takes at most 10ms.
639 WriteLock l(s_instanceBitsLock);
640 for (AllClasses ac; !ac.empty(); ) {
641 Class* c = ac.popFront();
642 c->setInstanceBitsAndParents();
645 s_instanceBitsInit.store(true, std::memory_order_release);
648 void Class::profileInstanceOf(const StringData* name) {
649 assert(name->isStatic());
650 unsigned inc = 1;
651 Class* c = Unit::lookupClass(name);
652 if (c && (c->attrs() & AttrInterface)) {
653 // Favor interfaces
654 inc = 250;
656 InstanceCounts::accessor acc;
658 // The extra layer of locking is here so that initInstanceBits can safely
659 // iterate over s_instanceCounts while building its map of names to bits.
660 ReadLock l(s_instanceCountsLock);
661 if (!s_instanceCounts.insert(acc, InstanceCounts::value_type(name, inc))) {
662 acc->second += inc;
666 bool Class::haveInstanceBit(const StringData* name) {
667 assert(Transl::Translator::WriteLease().amOwner());
668 assert(s_instanceBitsInit.load(std::memory_order_acquire));
669 return mapContains(s_instanceBits, name);
672 bool Class::getInstanceBitMask(const StringData* name,
673 int& offset, uint8& mask) {
674 assert(Transl::Translator::WriteLease().amOwner());
675 assert(s_instanceBitsInit.load(std::memory_order_acquire));
676 const size_t bitWidth = sizeof(mask) * CHAR_BIT;
677 unsigned bit;
678 if (!mapGet(s_instanceBits, name, &bit)) return false;
679 assert(bit >= 1 && bit < kInstanceBits);
680 offset = offsetof(Class, m_instanceBits) + bit / bitWidth * sizeof(mask);
681 mask = 1u << (bit % bitWidth);
682 return true;
686 * Check whether a Class from a previous request is available to be defined.
687 * The caller should check that it has the same preClass that is being defined.
688 * Being available means that the parent, the interfaces and the traits are
689 * already defined (or become defined via autoload, if tryAutoload is true).
691 * returns AvailTrue - if it is available
692 * AvailFail - if it is impossible to define the class at this point
693 * AvailFalse- if this particular Class* cant be defined at this point
695 * Note that AvailFail means that at least one of the parent, interfaces and
696 * traits was not defined at all, while AvailFalse means that at least one
697 * was defined but did not correspond to this Class*
699 * The parent parameter is used for two purposes: first it avoids looking up the
700 * active parent class for each potential Class*; and second its used on
701 * AvailFail to return the problem class so the caller can report the error
702 * correctly.
704 Class::Avail Class::avail(Class*& parent, bool tryAutoload /*=false*/) const {
705 if (Class *ourParent = m_parent.get()) {
706 if (!parent) {
707 PreClass *ppcls = ourParent->m_preClass.get();
708 parent = Unit::getClass(ppcls->namedEntity(), ppcls->name(), tryAutoload);
709 if (!parent) {
710 parent = ourParent;
711 return AvailFail;
714 if (parent != ourParent) return AvailFalse;
716 for (size_t i = 0, nInterfaces = m_declInterfaces.size();
717 i < nInterfaces; ++i) {
718 Class* declInterface = m_declInterfaces[i].get();
719 PreClass *pint = declInterface->m_preClass.get();
720 Class* interface = Unit::getClass(pint->namedEntity(), pint->name(),
721 tryAutoload);
722 if (interface != declInterface) {
723 if (interface == NULL) {
724 parent = declInterface;
725 return AvailFail;
727 return AvailFalse;
730 for (size_t i = 0; i < m_usedTraits.size(); i++) {
731 Class* usedTrait = m_usedTraits[i].get();
732 PreClass* ptrait = usedTrait->m_preClass.get();
733 Class* trait = Unit::getClass(ptrait->namedEntity(), ptrait->name(),
734 tryAutoload);
735 if (trait != usedTrait) {
736 if (trait == NULL) {
737 parent = usedTrait;
738 return AvailFail;
740 return AvailFalse;
743 return AvailTrue;
746 // If this Class represents the same class as 'preClass' or a descendent of
747 // 'preClass', this function returns the Class* that corresponds to 'preClass'.
748 // Otherwise, this function returns NULL.
749 Class* Class::classof(const PreClass* preClass) const {
750 Class* class_ = const_cast<Class*>(this);
751 do {
752 if (class_->m_preClass.get() == preClass) {
753 return class_;
755 std::vector<ClassPtr>& interfaces = class_->m_declInterfaces;
756 for (unsigned i = 0; i < interfaces.size(); ++i) {
757 // Interfaces can extend arbitrarily many interfaces themselves, so
758 // search them recursively
759 Class* iclass = interfaces[i]->classof(preClass);
760 if (iclass) {
761 return iclass;
764 class_ = class_->m_parent.get();
765 } while (class_ != NULL);
766 return NULL;
769 void Class::initialize(TypedValue*& sProps) const {
770 if (m_pinitVec.size() > 0) {
771 if (getPropData() == NULL) {
772 initProps();
775 // The asymmetry between the logic around initProps() above and initSProps()
776 // below is due to the fact that instance properties only require storage in
777 // g_vmContext if there are non-scalar initializers involved, whereas static
778 // properties *always* require storage in g_vmContext.
779 if (numStaticProperties() > 0) {
780 if ((sProps = getSPropData()) == NULL) {
781 sProps = initSProps();
783 } else {
784 sProps = NULL;
788 void Class::initialize() const {
789 TypedValue* sProps;
790 initialize(sProps);
793 Class::PropInitVec* Class::initPropsImpl() const {
794 assert(m_pinitVec.size() > 0);
795 assert(getPropData() == NULL);
796 // Copy initial values for properties to a new vector that can be used to
797 // complete initialization for non-scalar properties via the iterative
798 // 86pinit() calls below. 86pinit() takes a reference to an array that
799 // contains the initial property values; alias propVec inside propArr such
800 // that propVec contains complete property initialization values as soon as
801 // the 86pinit() calls are done.
802 PropInitVec* propVec = PropInitVec::allocInRequestArena(m_declPropInit);
803 size_t nProps = numDeclProperties();
805 // During property initialization, we provide access to the
806 // properties by name via this NameValueTable.
808 // Note that constructing these on the stack is slightly
809 // risky; we're relying on nobody getting hold of references
810 // to them. Also note that the assert on the refCount below
811 // is quite inadequate; the only way for these to leak is if
812 // an error occurs - but in that case, we wont reach the assert
813 // However, since we dont /want/ these to leak into the backtrace,
814 // we prevent it by setting AttrNoInjection (see Func::init)
815 NameValueTable propNvt(numDeclProperties());
816 NameValueTableWrapper propArr(&propNvt);
817 propArr.incRefCount();
819 // Create a sentinel that uniquely identifies uninitialized properties.
820 ObjectData* sentinel = SystemLib::AllocPinitSentinel();
821 sentinel->incRefCount();
822 // Insert propArr and sentinel into the args array, transferring ownership.
823 HphpArray* args = NEW(HphpArray)(2);
824 args->incRefCount();
826 TypedValue tv;
827 tv.m_data.parr = &propArr;
828 tv._count = 0;
829 tv.m_type = KindOfArray;
830 args->nvAppend(&tv);
831 propArr.decRefCount();
834 TypedValue tv;
835 tv.m_data.pobj = sentinel;
836 tv._count = 0;
837 tv.m_type = KindOfObject;
838 args->nvAppend(&tv);
839 sentinel->decRefCount();
841 TypedValue* tvSentinel = args->nvGetValueRef(1);
842 for (size_t i = 0; i < nProps; ++i) {
843 TypedValue& prop = (*propVec)[i];
844 if (prop.m_type == KindOfUninit) {
845 // Replace undefined values with tvSentinel, which acts as a
846 // unique sentinel for undefined properties in 86pinit().
847 tvDup(tvSentinel, &prop);
849 // We have to use m_originalMangledName here because the
850 // 86pinit methods for traits depend on it
851 const StringData* k = (m_declProperties[i].m_attrs & AttrPrivate)
852 ? m_declProperties[i].m_originalMangledName
853 : m_declProperties[i].m_name;
854 propNvt.migrateSet(k, &prop);
856 // Iteratively invoke 86pinit() methods upward through the inheritance chain.
857 for (Class::InitVec::const_reverse_iterator it = m_pinitVec.rbegin();
858 it != m_pinitVec.rend(); ++it) {
859 TypedValue retval;
860 g_vmContext->invokeFunc(&retval, *it, args, NULL, const_cast<Class*>(this));
861 assert(!IS_REFCOUNTED_TYPE(retval.m_type));
864 // Promote non-static arrays/strings (that came from 86pinit) to
865 // static. This allows us to use memcpy to initialize object
866 // properties, without conflicting with the refcounting that happens
867 // on object destruction.
868 for (PropInitVec::iterator it = propVec->begin();
869 it != propVec->end(); ++it) {
870 tvAsVariant(&(*it)).setEvalScalar();
873 // Free the args array. propArr is allocated on the stack so it
874 // better be only referenced from args.
875 assert(propArr.getCount() == 1);
876 assert(args->getCount() == 1);
877 decRefArr(args);
878 return propVec;
881 Slot Class::getDeclPropIndex(Class* ctx, const StringData* key,
882 bool& accessible) const {
883 Slot propInd = lookupDeclProp(key);
884 if (propInd != kInvalidSlot) {
885 Attr attrs = m_declProperties[propInd].m_attrs;
886 if ((attrs & (AttrProtected|AttrPrivate)) &&
887 !g_vmContext->getDebuggerBypassCheck()) {
888 // Fetch 'baseClass', which is the class in the inheritance
889 // tree which first declared the property
890 Class* baseClass = m_declProperties[propInd].m_class;
891 assert(baseClass);
892 // If ctx == baseClass, we know we have the right property
893 // and we can stop here.
894 if (ctx == baseClass) {
895 accessible = true;
896 return propInd;
898 // The anonymous context cannot access protected or private
899 // properties, so we can fail fast here.
900 if (ctx == NULL) {
901 accessible = false;
902 return propInd;
904 assert(ctx);
905 if (attrs & AttrPrivate) {
906 // ctx != baseClass and the property is private, so it is not
907 // accessible. We need to keep going because ctx may define a
908 // private property with this name.
909 accessible = false;
910 } else {
911 if (ctx->classof(baseClass)) {
912 // ctx is derived from baseClass, so we know this protected
913 // property is accessible and we know ctx cannot have private
914 // property with the same name, so we're done.
915 accessible = true;
916 return propInd;
918 if (!baseClass->classof(ctx)) {
919 // ctx is not the same, an ancestor, or a descendent of baseClass,
920 // so the property is not accessible. Also, we know that ctx cannot
921 // be the same or an ancestor of this, so we don't need to check if
922 // ctx declares a private property with the same name and we can
923 // fail fast here.
924 accessible = false;
925 return propInd;
927 // We now know this protected property is accessible, but we need to
928 // keep going because ctx may define a private property with the same
929 // name.
930 accessible = true;
931 assert(baseClass->classof(ctx));
933 } else {
934 // The property is public (or we're in the debugger and we are bypassing
935 // accessibility checks).
936 accessible = true;
937 // If ctx == this, we don't have to check if ctx defines a private
938 // property with the same name and we can stop here.
939 if (ctx == this) {
940 return propInd;
942 // We still need to check if ctx defines a private property with the
943 // same name.
945 } else {
946 // We didn't find a visible declared property in this's property map
947 accessible = false;
949 // If ctx is an ancestor of this, check if ctx has a private method
950 // with the same name.
951 if (ctx && classof(ctx)) {
952 Slot ctxPropInd = ctx->lookupDeclProp(key);
953 if (ctxPropInd != kInvalidSlot &&
954 ctx->m_declProperties[ctxPropInd].m_class == ctx &&
955 (ctx->m_declProperties[ctxPropInd].m_attrs & AttrPrivate)) {
956 // A private property from ctx trumps any other property we may
957 // have found.
958 accessible = true;
959 return ctxPropInd;
962 return propInd;
965 TypedValue* Class::initSPropsImpl() const {
966 assert(numStaticProperties() > 0);
967 assert(getSPropData() == NULL);
968 // Create an array that is initially large enough to hold all static
969 // properties.
970 TypedValue* const spropTable =
971 new (request_arena()) TypedValue[m_staticProperties.size()];
973 boost::optional<NameValueTable> nvt;
974 const bool hasNonscalarInit = !m_sinitVec.empty();
975 if (hasNonscalarInit) {
976 nvt = boost::in_place<NameValueTable>(m_staticProperties.size());
979 // Iteratively initialize properties. Non-scalar initializers are
980 // initialized to KindOfUninit here, and the 86sinit()-based initialization
981 // finishes the job later.
982 for (Slot slot = 0; slot < m_staticProperties.size(); ++slot) {
983 const SProp& sProp = m_staticProperties[slot];
985 TypedValue* storage = 0;
986 if (sProp.m_class == this) {
987 // Embed static property value directly in array.
988 assert(tvIsStatic(&sProp.m_val));
989 spropTable[slot] = sProp.m_val;
990 storage = &spropTable[slot];
991 } else {
992 // Alias parent class's static property.
993 bool visible, accessible;
994 storage = sProp.m_class->getSProp(NULL, sProp.m_name, visible,
995 accessible);
996 tvBindIndirect(&spropTable[slot], storage);
999 if (hasNonscalarInit) {
1000 nvt->migrateSet(sProp.m_name, storage);
1004 // Invoke 86sinit's if necessary, to handle non-scalar initializers.
1005 if (hasNonscalarInit) {
1006 // See note in initPropsImpl for why its ok to allocate
1007 // this on the stack.
1008 NameValueTableWrapper nvtWrapper(&*nvt);
1009 nvtWrapper.incRefCount();
1011 HphpArray* args = NEW(HphpArray)(1);
1012 args->incRefCount();
1013 try {
1015 TypedValue tv;
1016 tv.m_data.parr = &nvtWrapper;
1017 tv._count = 0;
1018 tv.m_type = KindOfArray;
1019 args->nvAppend(&tv);
1021 for (unsigned i = 0; i < m_sinitVec.size(); i++) {
1022 TypedValue retval;
1023 g_vmContext->invokeFunc(&retval, m_sinitVec[i], args, NULL,
1024 const_cast<Class*>(this));
1025 assert(!IS_REFCOUNTED_TYPE(retval.m_type));
1027 // Release the args array. nvtWrapper is on the stack, so it
1028 // better have a single reference.
1029 assert(args->getCount() == 1);
1030 args->release();
1031 assert(nvtWrapper.getCount() == 1);
1032 } catch (...) {
1033 assert(args->getCount() == 1);
1034 args->release();
1035 assert(nvtWrapper.getCount() == 1);
1036 throw;
1040 return spropTable;
1043 TypedValue* Class::getSProp(Class* ctx, const StringData* sPropName,
1044 bool& visible, bool& accessible) const {
1045 TypedValue* sProps;
1046 initialize(sProps);
1048 Slot sPropInd = lookupSProp(sPropName);
1049 if (sPropInd == kInvalidSlot) {
1050 // Non-existant property.
1051 visible = false;
1052 accessible = false;
1053 return NULL;
1056 visible = true;
1057 if (ctx == this) {
1058 // Property access is from within a method of this class, so the property
1059 // is accessible.
1060 accessible = true;
1061 } else {
1062 Attr sPropAttrs = m_staticProperties[sPropInd].m_attrs;
1063 if ((ctx != NULL) && (classof(ctx) || ctx->classof(this))) {
1064 // Property access is from within a parent class's method, which is
1065 // allowed for protected/public properties.
1066 switch (sPropAttrs & (AttrPublic|AttrProtected|AttrPrivate)) {
1067 case AttrPublic:
1068 case AttrProtected: accessible = true; break;
1069 case AttrPrivate:
1070 accessible = g_vmContext->getDebuggerBypassCheck(); break;
1071 default: not_reached();
1073 } else {
1074 // Property access is in an effectively anonymous context, so only public
1075 // properties are accessible.
1076 switch (sPropAttrs & (AttrPublic|AttrProtected|AttrPrivate)) {
1077 case AttrPublic: accessible = true; break;
1078 case AttrProtected:
1079 case AttrPrivate:
1080 accessible = g_vmContext->getDebuggerBypassCheck(); break;
1081 default: not_reached();
1086 assert(sProps != NULL);
1087 TypedValue* sProp = tvDerefIndirect(&sProps[sPropInd]);
1088 assert(sProp->m_type != KindOfUninit &&
1089 "static property initialization failed to initialize a property");
1090 return sProp;
1093 bool Class::IsPropAccessible(const Prop& prop, Class* ctx) {
1094 if (prop.m_attrs & AttrPublic) return true;
1095 if (prop.m_attrs & AttrPrivate) return prop.m_class == ctx;
1096 if (!ctx) return false;
1098 return prop.m_class->classof(ctx) || ctx->classof(prop.m_class);
1101 TypedValue Class::getStaticPropInitVal(const SProp& prop) {
1102 Class* declCls = prop.m_class;
1103 Slot s = declCls->m_staticProperties.findIndex(prop.m_name);
1104 assert(s != kInvalidSlot);
1105 return declCls->m_staticProperties[s].m_val;
1108 HphpArray* Class::initClsCnsData() const {
1109 Slot nConstants = m_constants.size();
1110 HphpArray* constants = NEW(HphpArray)(nConstants);
1111 constants->incRefCount();
1113 if (m_parent.get() != NULL) {
1114 if (g_vmContext->getClsCnsData(m_parent.get()) == NULL) {
1115 // Initialize recursively up the inheritance chain.
1116 m_parent->initClsCnsData();
1120 for (Slot i = 0; i < nConstants; ++i) {
1121 const Const& constant = m_constants[i];
1122 TypedValue* tv = (TypedValue*)&constant.m_val;
1123 constants->nvSet((StringData*)constant.m_name, tv, false);
1124 // XXX: nvSet() converts KindOfUninit to KindOfNull, but our class
1125 // constant logic needs to store KindOfUninit to indicate the the
1126 // constant's value has not been computed yet. We should find a better
1127 // way to deal with this.
1128 if (tv->m_type == KindOfUninit) {
1129 constants->nvGetValueRef(i)->m_type = KindOfUninit;
1133 g_vmContext->setClsCnsData(this, constants);
1134 return constants;
1137 TypedValue* Class::cnsNameToTV(const StringData* clsCnsName,
1138 Slot& clsCnsInd) const {
1139 clsCnsInd = m_constants.findIndex(clsCnsName);
1140 if (clsCnsInd == kInvalidSlot) {
1141 return NULL;
1143 return const_cast<TypedValue*>(&m_constants[clsCnsInd].m_val);
1146 TypedValue* Class::clsCnsGet(const StringData* clsCnsName) const {
1147 Slot clsCnsInd;
1148 TypedValue* clsCns = cnsNameToTV(clsCnsName, clsCnsInd);
1149 if (!clsCns || clsCns->m_type != KindOfUninit) {
1150 return clsCns;
1153 // This constant has a non-scalar initializer, so look in g_vmContext for
1154 // an entry associated with this class.
1155 HphpArray* clsCnsData = g_vmContext->getClsCnsData(this);
1156 if (clsCnsData == NULL) {
1157 clsCnsData = initClsCnsData();
1160 clsCns = clsCnsData->nvGetValueRef(clsCnsInd);
1161 if (clsCns->m_type == KindOfUninit) {
1162 // The class constant has not been initialized yet; do so.
1163 static StringData* sd86cinit = StringData::GetStaticString("86cinit");
1164 const Func* meth86cinit =
1165 m_constants[clsCnsInd].m_class->lookupMethod(sd86cinit);
1166 TypedValue tv;
1167 tv.m_data.pstr = (StringData*)clsCnsName;
1168 tv._count = 0;
1169 tv.m_type = KindOfString;
1170 g_vmContext->invokeFunc(clsCns, meth86cinit,
1171 CREATE_VECTOR1(tvAsCVarRef(&tv)), NULL,
1172 const_cast<Class*>(this));
1174 return clsCns;
1178 * Class::clsCnsType: provide the current runtime type of this class
1179 * constant. This has predictive value for the translator.
1181 DataType Class::clsCnsType(const StringData* cnsName) const {
1182 Slot slot;
1183 TypedValue* cns = cnsNameToTV(cnsName, slot);
1184 if (!cns) return KindOfUninit;
1185 return cns->m_type;
1188 void Class::setParent() {
1189 // Validate the parent
1190 if (m_parent.get() != NULL) {
1191 Attr attrs = m_parent->attrs();
1192 if (UNLIKELY(attrs & (AttrFinal | AttrInterface | AttrTrait))) {
1193 static StringData* sd___MockClass =
1194 StringData::GetStaticString("__MockClass");
1195 if (!(attrs & AttrFinal) ||
1196 m_preClass->userAttributes().find(sd___MockClass) ==
1197 m_preClass->userAttributes().end()) {
1198 raise_error("Class %s may not inherit from %s (%s)",
1199 m_preClass->name()->data(),
1200 ((attrs & AttrFinal) ? "final class" :
1201 (attrs & AttrInterface) ? "interface" : "trait"),
1202 m_parent->name()->data());
1206 // Cache m_preClass->attrs()
1207 m_attrCopy = m_preClass->attrs();
1208 // Handle stuff specific to cppext classes
1209 if (m_preClass->instanceCtor()) {
1210 m_InstanceCtor = m_preClass->instanceCtor();
1211 m_builtinPropSize = m_preClass->builtinPropSize();
1212 m_clsInfo = ClassInfo::FindSystemClassInterfaceOrTrait(nameRef());
1213 } else if (m_parent.get()) {
1214 m_InstanceCtor = m_parent->m_InstanceCtor;
1215 m_builtinPropSize = m_parent->m_builtinPropSize;
1219 static Func* findSpecialMethod(Class* cls, const StringData* name) {
1220 if (!cls->preClass()->hasMethod(name)) return NULL;
1221 Func* f = cls->preClass()->lookupMethod(name);
1222 f = f->clone();
1223 f->setNewFuncId();
1224 f->setCls(cls);
1225 f->setBaseCls(cls);
1226 f->setHasPrivateAncestor(false);
1227 return f;
1230 void Class::setSpecial() {
1231 static StringData* sd_toString = StringData::GetStaticString("__toString");
1232 static StringData* sd_uuconstruct =
1233 StringData::GetStaticString("__construct");
1234 static StringData* sd_uudestruct =
1235 StringData::GetStaticString("__destruct");
1237 m_toString = lookupMethod(sd_toString);
1238 m_dtor = lookupMethod(sd_uudestruct);
1240 // Look for __construct() declared in either this class or a trait
1241 Func* fConstruct = lookupMethod(sd_uuconstruct);
1242 if (fConstruct && (fConstruct->preClass() == m_preClass.get() ||
1243 fConstruct->preClass()->attrs() & AttrTrait)) {
1244 m_ctor = fConstruct;
1245 return;
1248 if (!(attrs() & AttrTrait)) {
1249 // Look for Foo::Foo() declared in this class (cannot be via trait).
1250 Func* fNamedCtor = lookupMethod(m_preClass->name());
1251 if (fNamedCtor && fNamedCtor->preClass() == m_preClass.get() &&
1252 !(fNamedCtor->attrs() & AttrTrait)) {
1254 Note: AttrTrait was set by the emitter if hphpc inlined a trait
1255 method into a class (WholeProgram mode only), so that we dont
1256 accidently mark it as a constructor here
1258 m_ctor = fNamedCtor;
1259 return;
1263 // Look for parent constructor other than 86ctor().
1264 if (m_parent.get() != NULL &&
1265 m_parent->m_ctor->name() != sd86ctor) {
1266 m_ctor = m_parent->m_ctor;
1267 return;
1270 // Use 86ctor(), since no program-supplied constructor exists
1271 m_ctor = findSpecialMethod(this, sd86ctor);
1272 assert(m_ctor && "class had no user-defined constructor or 86ctor");
1273 assert(m_ctor->attrs() == (AttrPublic|AttrNoInjection));
1276 void Class::applyTraitPrecRule(const PreClass::TraitPrecRule& rule) {
1277 const StringData* methName = rule.getMethodName();
1278 const StringData* selectedTraitName = rule.getSelectedTraitName();
1279 TraitNameSet otherTraitNames;
1280 rule.getOtherTraitNames(otherTraitNames);
1282 MethodToTraitListMap::iterator methIter =
1283 m_importMethToTraitMap.find(methName);
1284 if (methIter == m_importMethToTraitMap.end()) {
1285 raise_error("unknown method '%s'", methName->data());
1288 bool foundSelectedTrait = false;
1290 TraitMethodList &methList = methIter->second;
1291 for (TraitMethodList::iterator nextTraitIter = methList.begin();
1292 nextTraitIter != methList.end(); ) {
1293 TraitMethodList::iterator traitIter = nextTraitIter++;
1294 const StringData* availTraitName = traitIter->m_trait->name();
1295 if (availTraitName == selectedTraitName) {
1296 foundSelectedTrait = true;
1297 } else {
1298 if (otherTraitNames.find(availTraitName) != otherTraitNames.end()) {
1299 otherTraitNames.erase(availTraitName);
1300 methList.erase(traitIter);
1305 // Check error conditions
1306 if (!foundSelectedTrait) {
1307 raise_error("unknown trait '%s'", selectedTraitName->data());
1309 if (otherTraitNames.size()) {
1310 raise_error("unknown trait '%s'", (*otherTraitNames.begin())->data());
1314 ClassPtr Class::findSingleTraitWithMethod(const StringData* methName) {
1315 // Note: m_methods includes methods from parents / traits recursively
1316 ClassPtr traitCls = ClassPtr();
1317 for (size_t t = 0; t < m_usedTraits.size(); t++) {
1318 if (m_usedTraits[t]->m_methods.contains(methName)) {
1319 if (traitCls.get() != NULL) { // more than one trait contains method
1320 return ClassPtr();
1322 traitCls = m_usedTraits[t];
1325 return traitCls;
1328 void Class::setImportTraitMethodModifiers(const StringData* methName,
1329 ClassPtr traitCls,
1330 Attr modifiers) {
1331 TraitMethodList &methList = m_importMethToTraitMap[methName];
1333 for (TraitMethodList::iterator iter = methList.begin();
1334 iter != methList.end(); iter++) {
1335 if (iter->m_trait.get() == traitCls.get()) {
1336 iter->m_modifiers = modifiers;
1337 return;
1342 // Keep track of trait aliases in the class to support
1343 // ReflectionClass::getTraitAliases
1344 void Class::addTraitAlias(const StringData* traitName,
1345 const StringData* origMethName,
1346 const StringData* newMethName) {
1347 char buf[traitName->size() + origMethName->size() + 9];
1348 sprintf(buf, "%s::%s", (traitName->empty() ? "(null)" : traitName->data()),
1349 origMethName->data());
1350 const StringData* origName = StringData::GetStaticString(buf);
1351 m_traitAliases.push_back(std::pair<const StringData*, const StringData*>
1352 (newMethName, origName));
1355 void Class::applyTraitAliasRule(const PreClass::TraitAliasRule& rule) {
1356 const StringData* traitName = rule.getTraitName();
1357 const StringData* origMethName = rule.getOrigMethodName();
1358 const StringData* newMethName = rule.getNewMethodName();
1360 ClassPtr traitCls;
1361 if (traitName->empty()) {
1362 traitCls = findSingleTraitWithMethod(origMethName);
1363 } else {
1364 traitCls = Unit::loadClass(traitName);
1367 if (!traitCls.get() || (!(traitCls->attrs() & AttrTrait))) {
1368 raise_error("unknown trait '%s'", traitName->data());
1371 // Save info to support ReflectionClass::getTraitAliases
1372 addTraitAlias(traitName, origMethName, newMethName);
1374 Func* traitMeth = traitCls->lookupMethod(origMethName);
1375 if (!traitMeth) {
1376 raise_error("unknown trait method '%s'", origMethName->data());
1379 Attr ruleModifiers;
1380 if (origMethName == newMethName) {
1381 ruleModifiers = rule.getModifiers();
1382 setImportTraitMethodModifiers(origMethName, traitCls, ruleModifiers);
1383 } else {
1384 ruleModifiers = rule.getModifiers();
1385 TraitMethod traitMethod(traitCls, traitMeth, ruleModifiers);
1386 addImportTraitMethod(traitMethod, newMethName);
1388 if (ruleModifiers & AttrStatic) {
1389 raise_error("cannot use 'static' as access modifier");
1393 void Class::applyTraitRules() {
1394 for (size_t i = 0; i < m_preClass->traitPrecRules().size(); i++) {
1395 applyTraitPrecRule(m_preClass->traitPrecRules()[i]);
1397 for (size_t i = 0; i < m_preClass->traitAliasRules().size(); i++) {
1398 applyTraitAliasRule(m_preClass->traitAliasRules()[i]);
1402 void Class::addImportTraitMethod(const TraitMethod &traitMethod,
1403 const StringData *methName) {
1404 if (!Func::isSpecial(methName)) {
1405 m_importMethToTraitMap[methName].push_back(traitMethod);
1409 void Class::importTraitMethod(const TraitMethod& traitMethod,
1410 const StringData* methName,
1411 MethodMap::Builder& builder) {
1412 ClassPtr trait = traitMethod.m_trait;
1413 Func* method = traitMethod.m_method;
1414 Attr modifiers = traitMethod.m_modifiers;
1416 MethodMap::Builder::iterator mm_iter = builder.find(methName);
1417 // For abstract methods, simply return if method already declared
1418 if ((modifiers & AttrAbstract) && mm_iter != builder.end()) {
1419 return;
1422 if (modifiers == AttrNone) {
1423 modifiers = method->attrs();
1424 } else {
1425 // Trait alias statements are only allowed to change the attributes that
1426 // are part 'attrMask' below; all other method attributes are preserved
1427 Attr attrMask = (Attr)(AttrPublic | AttrProtected | AttrPrivate |
1428 AttrAbstract | AttrFinal);
1429 modifiers = (Attr)((modifiers & (attrMask)) |
1430 (method->attrs() & ~(attrMask)));
1433 Func* parentMethod = NULL;
1434 if (mm_iter != builder.end()) {
1435 Func* existingMethod = builder[mm_iter->second];
1436 if (existingMethod->cls() == this) {
1437 // Don't override an existing method if this class provided an
1438 // implementation
1439 return;
1441 parentMethod = existingMethod;
1443 Func* f = method->clone();
1444 f->setNewFuncId();
1445 f->setClsAndName(this, methName);
1446 f->setAttrs(modifiers);
1447 if (!parentMethod) {
1448 // New method
1449 builder.add(methName, f);
1450 f->setBaseCls(this);
1451 f->setHasPrivateAncestor(false);
1452 } else {
1453 // Override an existing method
1454 Class* baseClass;
1456 methodOverrideCheck(parentMethod, f);
1458 assert(!(f->attrs() & AttrPrivate) ||
1459 (parentMethod->attrs() & AttrPrivate));
1460 if ((parentMethod->attrs() & AttrPrivate) || (f->attrs() & AttrPrivate)) {
1461 baseClass = this;
1462 } else {
1463 baseClass = parentMethod->baseCls();
1465 f->setBaseCls(baseClass);
1466 f->setHasPrivateAncestor(
1467 parentMethod->hasPrivateAncestor() ||
1468 (parentMethod->attrs() & AttrPrivate));
1469 builder[mm_iter->second] = f;
1473 // This method removes trait abstract methods that are either:
1474 // 1) implemented by other traits
1475 // 2) duplicate
1476 void Class::removeSpareTraitAbstractMethods() {
1478 for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin();
1479 iter != m_importMethToTraitMap.end(); iter++) {
1481 TraitMethodList& tMethList = iter->second;
1482 bool hasNonAbstractMeth = false;
1483 unsigned countAbstractMeths = 0;
1484 for (TraitMethodList::const_iterator traitMethIter = tMethList.begin();
1485 traitMethIter != tMethList.end(); traitMethIter++) {
1486 if (!(traitMethIter->m_modifiers & AttrAbstract)) {
1487 hasNonAbstractMeth = true;
1488 } else {
1489 countAbstractMeths++;
1492 if (hasNonAbstractMeth || countAbstractMeths > 1) {
1493 // Erase spare abstract declarations
1494 bool firstAbstractMeth = true;
1495 for (TraitMethodList::iterator nextTraitIter = tMethList.begin();
1496 nextTraitIter != tMethList.end(); ) {
1497 TraitMethodList::iterator traitIter = nextTraitIter++;
1498 if (traitIter->m_modifiers & AttrAbstract) {
1499 if (hasNonAbstractMeth || !firstAbstractMeth) {
1500 tMethList.erase(traitIter);
1502 firstAbstractMeth = false;
1509 // fatals on error
1510 void Class::importTraitMethods(MethodMap::Builder& builder) {
1511 // 1. Find all methods to be imported
1512 for (size_t t = 0; t < m_usedTraits.size(); t++) {
1513 ClassPtr trait = m_usedTraits[t];
1514 for (Slot i = 0; i < trait->m_methods.size(); ++i) {
1515 Func* method = trait->m_methods[i];
1516 const StringData* methName = method->name();
1517 TraitMethod traitMethod(trait, method, method->attrs());
1518 addImportTraitMethod(traitMethod, methName);
1522 // 2. Apply trait rules
1523 applyTraitRules();
1525 // 3. Remove abstract methods provided by other traits, and also duplicates
1526 removeSpareTraitAbstractMethods();
1528 // 4. Actually import the methods
1529 for (MethodToTraitListMap::const_iterator iter =
1530 m_importMethToTraitMap.begin();
1531 iter != m_importMethToTraitMap.end(); iter++) {
1533 // The rules may rule out a method from all traits.
1534 // In this case, simply don't import the method.
1535 if (iter->second.size() == 0) {
1536 continue;
1539 // Consistency checking: each name must only refer to one imported method
1540 if (iter->second.size() > 1) {
1541 // OK if the class will override the method...
1542 if (m_preClass->hasMethod(iter->first)) continue;
1544 raise_error("method '%s' declared in multiple traits",
1545 iter->first->data());
1548 TraitMethodList::const_iterator traitMethIter = iter->second.begin();
1549 importTraitMethod(*traitMethIter, iter->first, builder);
1554 void Class::methodOverrideCheck(const Func* parentMethod, const Func* method) {
1555 // Skip special methods
1556 if (isdigit((uchar)method->name()->data()[0])) return;
1558 if ((parentMethod->attrs() & AttrFinal)) {
1559 static StringData* sd___MockClass =
1560 StringData::GetStaticString("__MockClass");
1561 if (m_preClass->userAttributes().find(sd___MockClass) ==
1562 m_preClass->userAttributes().end()) {
1563 raise_error("Cannot override final method %s::%s()",
1564 m_parent->name()->data(), parentMethod->name()->data());
1568 if (method->attrs() & AttrAbstract) {
1569 raise_error("Cannot re-declare %sabstract method %s::%s() abstract in "
1570 "class %s",
1571 (parentMethod->attrs() & AttrAbstract) ? "" : "non-",
1572 m_parent->m_preClass->name()->data(),
1573 parentMethod->name()->data(), m_preClass->name()->data());
1576 if ((method->attrs() & (AttrPublic | AttrProtected | AttrPrivate)) >
1577 (parentMethod->attrs() & (AttrPublic | AttrProtected | AttrPrivate))) {
1578 raise_error(
1579 "Access level to %s::%s() must be %s (as in class %s) or weaker",
1580 m_preClass->name()->data(), method->name()->data(),
1581 attrToVisibilityStr(parentMethod->attrs()),
1582 m_parent->name()->data());
1585 if ((method->attrs() & AttrStatic) != (parentMethod->attrs() & AttrStatic)) {
1586 raise_error("Cannot change %sstatic method %s::%s() to %sstatic in %s",
1587 (parentMethod->attrs() & AttrStatic) ? "" : "non-",
1588 parentMethod->baseCls()->name()->data(),
1589 method->name()->data(),
1590 (method->attrs() & AttrStatic) ? "" : "non-",
1591 m_preClass->name()->data());
1594 Func* baseMethod = parentMethod->baseCls()->lookupMethod(method->name());
1595 if (!(method->attrs() & AttrAbstract) &&
1596 (baseMethod->attrs() & AttrAbstract) &&
1597 (!hphpiCompat || strcmp(method->name()->data(), "__construct"))) {
1598 method->parametersCompat(m_preClass.get(), baseMethod);
1602 void Class::setMethods() {
1603 std::vector<Slot> parentMethodsWithStaticLocals;
1604 MethodMap::Builder builder;
1606 if (m_parent.get() != NULL) {
1607 // Copy down the parent's method entries. These may be overridden below.
1608 for (Slot i = 0; i < m_parent->m_methods.size(); ++i) {
1609 Func* f = m_parent->m_methods[i];
1610 assert(f);
1611 if ((f->attrs() & AttrClone) ||
1612 (!(f->attrs() & AttrPrivate) && f->hasStaticLocals())) {
1613 // When copying down an entry for a non-private method that has
1614 // static locals, we want to make a copy of the Func so that it
1615 // gets a distinct set of static locals variables. We defer making
1616 // a copy of the parent method until the end because it might get
1617 // overriden below.
1618 parentMethodsWithStaticLocals.push_back(i);
1620 assert(builder.size() == i);
1621 builder.add(f->name(), f);
1625 assert(AttrPublic < AttrProtected && AttrProtected < AttrPrivate);
1626 // Overlay/append this class's public/protected methods onto/to those of the
1627 // parent.
1628 for (size_t methI = 0; methI < m_preClass->numMethods(); ++methI) {
1629 Func* method = m_preClass->methods()[methI];
1630 if (Func::isSpecial(method->name())) {
1631 if (method->name() == sd86ctor ||
1632 method->name() == sd86sinit ||
1633 method->name() == sd86pinit) {
1635 * we could also skip the cinit function here, but
1636 * that would mean storing it somewhere else.
1638 continue;
1641 MethodMap::Builder::iterator it2 = builder.find(method->name());
1642 if (it2 != builder.end()) {
1643 Func* parentMethod = builder[it2->second];
1644 // We should never have null func pointers to deal with
1645 assert(parentMethod);
1646 methodOverrideCheck(parentMethod, method);
1647 // Overlay.
1648 Func* f = method->clone();
1649 f->setNewFuncId();
1650 f->setCls(this);
1651 Class* baseClass;
1652 assert(!(f->attrs() & AttrPrivate) ||
1653 (parentMethod->attrs() & AttrPrivate));
1654 if ((parentMethod->attrs() & AttrPrivate) || (f->attrs() & AttrPrivate)) {
1655 baseClass = this;
1656 } else {
1657 baseClass = parentMethod->baseCls();
1659 f->setBaseCls(baseClass);
1660 f->setHasPrivateAncestor(
1661 parentMethod->hasPrivateAncestor() ||
1662 (parentMethod->attrs() & AttrPrivate));
1663 builder[it2->second] = f;
1664 } else {
1665 // This is the first class that declares the method
1666 Class* baseClass = this;
1667 // Append.
1668 Func* f = method->clone();
1669 f->setNewFuncId();
1670 f->setCls(this);
1671 f->setBaseCls(baseClass);
1672 f->setHasPrivateAncestor(false);
1673 builder.add(method->name(), f);
1677 m_traitsBeginIdx = builder.size();
1678 if (m_usedTraits.size()) {
1679 importTraitMethods(builder);
1681 m_traitsEndIdx = builder.size();
1683 // Make copies of Funcs inherited from the parent class that have
1684 // static locals
1685 std::vector<Slot>::const_iterator it;
1686 for (it = parentMethodsWithStaticLocals.begin();
1687 it != parentMethodsWithStaticLocals.end(); ++it) {
1688 Func*& f = builder[*it];
1689 if (f->cls() != this) {
1690 // Don't update f's m_cls if it doesn't have AttrClone set:
1691 // we're cloning it so that we get a distinct set of static
1692 // locals and a separate translation, not a different context
1693 // class.
1694 f = f->clone();
1695 if (f->attrs() & AttrClone) {
1696 f->setCls(this);
1698 f->setNewFuncId();
1702 // If class is not abstract, check that all abstract methods have been defined
1703 if (!(attrs() & (AttrTrait | AttrInterface | AttrAbstract))) {
1704 for (Slot i = 0; i < builder.size(); i++) {
1705 const Func* meth = builder[i];
1706 if (meth->attrs() & AttrAbstract) {
1707 raise_error("Class %s contains abstract method (%s) and "
1708 "must therefore be declared abstract or implement "
1709 "the remaining methods", m_preClass->name()->data(),
1710 meth->name()->data());
1715 m_methods.create(builder);
1716 for (Slot i = 0; i < m_methods.size(); ++i) {
1717 m_methods[i]->setMethodSlot(i);
1721 void Class::setODAttributes() {
1722 static StringData* sd__sleep = StringData::GetStaticString("__sleep");
1723 static StringData* sd__get = StringData::GetStaticString("__get");
1724 static StringData* sd__set = StringData::GetStaticString("__set");
1725 static StringData* sd__isset = StringData::GetStaticString("__isset");
1726 static StringData* sd__unset = StringData::GetStaticString("__unset");
1727 static StringData* sd___lval = StringData::GetStaticString("___lval");
1728 static StringData* sd__call = StringData::GetStaticString("__call");
1729 static StringData* sd__callStatic
1730 = StringData::GetStaticString("__callStatic");
1732 m_ODAttrs = 0;
1733 if (lookupMethod(sd__sleep )) { m_ODAttrs |= ObjectData::HasSleep; }
1734 if (lookupMethod(sd__get )) { m_ODAttrs |= ObjectData::UseGet; }
1735 if (lookupMethod(sd__set )) { m_ODAttrs |= ObjectData::UseSet; }
1736 if (lookupMethod(sd__isset )) { m_ODAttrs |= ObjectData::UseIsset; }
1737 if (lookupMethod(sd__unset )) { m_ODAttrs |= ObjectData::UseUnset; }
1738 if (lookupMethod(sd___lval )) { m_ODAttrs |= ObjectData::HasLval; }
1739 if (lookupMethod(sd__call )) { m_ODAttrs |= ObjectData::HasCall; }
1740 if (lookupMethod(sd__callStatic)) { m_ODAttrs |= ObjectData::HasCallStatic; }
1743 void Class::setConstants() {
1744 ConstMap::Builder builder;
1746 if (m_parent.get() != NULL) {
1747 for (Slot i = 0; i < m_parent->m_constants.size(); ++i) {
1748 // Copy parent's constants.
1749 builder.add(m_parent->m_constants[i].m_name, m_parent->m_constants[i]);
1753 // Copy in interface constants.
1754 for (std::vector<ClassPtr>::const_iterator it = m_declInterfaces.begin();
1755 it != m_declInterfaces.end(); ++it) {
1756 for (Slot slot = 0; slot < (*it)->m_constants.size(); ++slot) {
1757 const Const& iConst = (*it)->m_constants[slot];
1759 // If you're inheriting a constant with the same name as an
1760 // existing one, they must originate from the same place.
1761 ConstMap::Builder::iterator existing = builder.find(iConst.m_name);
1762 if (existing != builder.end() &&
1763 builder[existing->second].m_class != iConst.m_class) {
1764 raise_error("Cannot inherit previously-inherited constant %s",
1765 iConst.m_name->data());
1768 builder.add(iConst.m_name, iConst);
1772 for (Slot i = 0, sz = m_preClass->numConstants(); i < sz; ++i) {
1773 const PreClass::Const* preConst = &m_preClass->constants()[i];
1774 ConstMap::Builder::iterator it2 = builder.find(preConst->name());
1775 if (it2 != builder.end()) {
1776 if (!(builder[it2->second].m_class->attrs() & AttrInterface)) {
1777 // Overlay ancestor's constant, only if it was not an interface const.
1778 builder[it2->second].m_class = this;
1779 builder[it2->second].m_val = preConst->val();
1780 } else {
1781 raise_error("Cannot override previously defined constant %s::%s in %s",
1782 builder[it2->second].m_class->name()->data(),
1783 preConst->name()->data(),
1784 m_preClass->name()->data());
1786 } else {
1787 // Append constant.
1788 Const constant;
1789 constant.m_class = this;
1790 constant.m_name = preConst->name();
1791 constant.m_val = preConst->val();
1792 constant.m_phpCode = preConst->phpCode();
1793 builder.add(preConst->name(), constant);
1797 m_constants.create(builder);
1800 void Class::setProperties() {
1801 int numInaccessible = 0;
1802 PropMap::Builder curPropMap;
1803 SPropMap::Builder curSPropMap;
1805 if (m_parent.get() != NULL) {
1806 for (Slot slot = 0; slot < m_parent->m_declProperties.size(); ++slot) {
1807 const Prop& parentProp = m_parent->m_declProperties[slot];
1809 // Copy parent's declared property. Protected properties may be
1810 // weakened to public below, but otherwise, the parent's properties
1811 // will stay the same for this class.
1812 Prop prop;
1813 prop.m_class = parentProp.m_class;
1814 prop.m_mangledName = parentProp.m_mangledName;
1815 prop.m_originalMangledName = parentProp.m_originalMangledName;
1816 prop.m_attrs = parentProp.m_attrs;
1817 prop.m_docComment = parentProp.m_docComment;
1818 prop.m_name = parentProp.m_name;
1819 if (!(parentProp.m_attrs & AttrPrivate)) {
1820 curPropMap.add(prop.m_name, prop);
1821 } else {
1822 ++numInaccessible;
1823 curPropMap.addUnnamed(prop);
1826 m_declPropInit = m_parent->m_declPropInit;
1827 for (Slot slot = 0; slot < m_parent->m_staticProperties.size(); ++slot) {
1828 const SProp& parentProp = m_parent->m_staticProperties[slot];
1829 if (parentProp.m_attrs & AttrPrivate) continue;
1831 // Alias parent's static property.
1832 SProp sProp;
1833 sProp.m_name = parentProp.m_name;
1834 sProp.m_attrs = parentProp.m_attrs;
1835 sProp.m_docComment = parentProp.m_docComment;
1836 sProp.m_class = parentProp.m_class;
1837 tvWriteUninit(&sProp.m_val);
1838 curSPropMap.add(sProp.m_name, sProp);
1842 assert(AttrPublic < AttrProtected && AttrProtected < AttrPrivate);
1843 for (Slot slot = 0; slot < m_preClass->numProperties(); ++slot) {
1844 const PreClass::Prop* preProp = &m_preClass->properties()[slot];
1846 if (!(preProp->attrs() & AttrStatic)) {
1847 // Overlay/append this class's protected and public properties onto/to
1848 // those of the parent, and append this class's private properties.
1849 // Append order doesn't matter here (unlike in setMethods()).
1851 // Prohibit static-->non-static redeclaration.
1852 SPropMap::Builder::iterator it2 = curSPropMap.find(preProp->name());
1853 if (it2 != curSPropMap.end()) {
1854 raise_error("Cannot redeclare static %s::$%s as non-static %s::$%s",
1855 curSPropMap[it2->second].m_class->name()->data(),
1856 preProp->name()->data(), m_preClass->name()->data(),
1857 preProp->name()->data());
1859 // Get parent's equivalent property, if one exists.
1860 const Prop* parentProp = NULL;
1861 if (m_parent.get() != NULL) {
1862 Slot id = m_parent->m_declProperties.findIndex(preProp->name());
1863 if (id != kInvalidSlot) {
1864 parentProp = &m_parent->m_declProperties[id];
1867 // Prohibit strengthening.
1868 if (parentProp
1869 && (preProp->attrs() & (AttrPublic|AttrProtected|AttrPrivate))
1870 > (parentProp->m_attrs & (AttrPublic|AttrProtected|AttrPrivate))) {
1871 raise_error(
1872 "Access level to %s::$%s() must be %s (as in class %s) or weaker",
1873 m_preClass->name()->data(), preProp->name()->data(),
1874 attrToVisibilityStr(parentProp->m_attrs),
1875 m_parent->name()->data());
1877 switch (preProp->attrs() & (AttrPublic|AttrProtected|AttrPrivate)) {
1878 case AttrPrivate: {
1879 // Append a new private property.
1880 Prop prop;
1881 prop.m_name = preProp->name();
1882 prop.m_mangledName = preProp->mangledName();
1883 prop.m_originalMangledName = preProp->mangledName();
1884 prop.m_attrs = preProp->attrs();
1885 // This is the first class to declare this property
1886 prop.m_class = this;
1887 prop.m_docComment = preProp->docComment();
1888 curPropMap.add(preProp->name(), prop);
1889 m_declPropInit.push_back(m_preClass->lookupProp(preProp->name())
1890 ->val());
1891 break;
1893 case AttrProtected: {
1894 // Check whether a superclass has already declared this protected
1895 // property.
1896 PropMap::Builder::iterator it2 = curPropMap.find(preProp->name());
1897 if (it2 != curPropMap.end()) {
1898 assert((curPropMap[it2->second].m_attrs
1899 & (AttrPublic|AttrProtected|AttrPrivate)) == AttrProtected);
1900 m_declPropInit[it2->second] = m_preClass->lookupProp(preProp
1901 ->name())->val();
1902 break;
1904 // Append a new protected property.
1905 Prop prop;
1906 prop.m_name = preProp->name();
1907 prop.m_mangledName = preProp->mangledName();
1908 prop.m_originalMangledName = preProp->mangledName();
1909 prop.m_attrs = preProp->attrs();
1910 // This is the first class to declare this property
1911 prop.m_class = this;
1912 prop.m_docComment = preProp->docComment();
1913 curPropMap.add(preProp->name(), prop);
1914 m_declPropInit.push_back(m_preClass->lookupProp(preProp->name())
1915 ->val());
1916 break;
1918 case AttrPublic: {
1919 // Check whether a superclass has already declared this as a
1920 // protected/public property.
1921 PropMap::Builder::iterator it2 = curPropMap.find(preProp->name());
1922 if (it2 != curPropMap.end()) {
1923 Prop& prop = curPropMap[it2->second];
1924 if ((prop.m_attrs & (AttrPublic|AttrProtected|AttrPrivate))
1925 == AttrProtected) {
1926 // Weaken protected property to public.
1927 prop.m_mangledName = preProp->mangledName();
1928 prop.m_originalMangledName = preProp->mangledName();
1929 prop.m_attrs = Attr(prop.m_attrs ^ (AttrProtected|AttrPublic));
1931 m_declPropInit[it2->second] = m_preClass->lookupProp(preProp
1932 ->name())->val();
1933 break;
1935 // Append a new public property.
1936 Prop prop;
1937 prop.m_name = preProp->name();
1938 prop.m_mangledName = preProp->mangledName();
1939 prop.m_originalMangledName = preProp->mangledName();
1940 prop.m_attrs = preProp->attrs();
1941 // This is the first class to declare this property
1942 prop.m_class = this;
1943 prop.m_docComment = preProp->docComment();
1944 curPropMap.add(preProp->name(), prop);
1945 m_declPropInit.push_back(m_preClass->lookupProp(preProp->name())
1946 ->val());
1947 break;
1949 default: assert(false);
1951 } else { // Static property.
1952 // Prohibit non-static-->static redeclaration.
1953 PropMap::Builder::iterator it2 = curPropMap.find(preProp->name());
1954 if (it2 != curPropMap.end()) {
1955 // Find class that declared non-static property.
1956 Class* ancestor;
1957 for (ancestor = m_parent.get();
1958 !ancestor->m_preClass->hasProp(preProp->name());
1959 ancestor = ancestor->m_parent.get()) {
1961 raise_error("Cannot redeclare non-static %s::$%s as static %s::$%s",
1962 ancestor->name()->data(),
1963 preProp->name()->data(),
1964 m_preClass->name()->data(),
1965 preProp->name()->data());
1967 // Get parent's equivalent property, if one exists.
1968 SPropMap::Builder::iterator it3 = curSPropMap.find(preProp->name());
1969 Slot sPropInd = kInvalidSlot;
1970 // Prohibit strengthening.
1971 if (it3 != curSPropMap.end()) {
1972 const SProp& parentSProp = curSPropMap[it3->second];
1973 if ((preProp->attrs() & (AttrPublic|AttrProtected|AttrPrivate))
1974 > (parentSProp.m_attrs & (AttrPublic|AttrProtected|AttrPrivate))) {
1975 raise_error(
1976 "Access level to %s::$%s() must be %s (as in class %s) or weaker",
1977 m_preClass->name()->data(), preProp->name()->data(),
1978 attrToVisibilityStr(parentSProp.m_attrs),
1979 m_parent->name()->data());
1981 sPropInd = it3->second;
1983 // Create a new property, or overlay ancestor's property if one exists.
1984 if (sPropInd == kInvalidSlot) {
1985 SProp sProp;
1986 sProp.m_name = preProp->name();
1987 sPropInd = curSPropMap.size();
1988 curSPropMap.add(sProp.m_name, sProp);
1990 SProp& sProp = curSPropMap[sPropInd];
1991 // Finish initializing.
1992 sProp.m_attrs = preProp->attrs();
1993 sProp.m_docComment = preProp->docComment();
1994 sProp.m_class = this;
1995 sProp.m_val = m_preClass->lookupProp(preProp->name())->val();
1999 importTraitProps(curPropMap, curSPropMap);
2001 m_declProperties.create(curPropMap);
2002 m_staticProperties.create(curSPropMap);
2004 m_declPropNumAccessible = m_declProperties.size() - numInaccessible;
2007 bool Class::compatibleTraitPropInit(TypedValue& tv1, TypedValue& tv2) {
2008 if (tv1.m_type != tv2.m_type) return false;
2009 switch (tv1.m_type) {
2010 case KindOfNull: return true;
2011 case KindOfBoolean:
2012 case KindOfInt64:
2013 case KindOfDouble:
2014 case KindOfStaticString:
2015 case KindOfString:
2016 return same(tvAsVariant(&tv1), tvAsVariant(&tv2));
2017 default: return false;
2021 void Class::importTraitInstanceProp(ClassPtr trait,
2022 Prop& traitProp,
2023 TypedValue& traitPropVal,
2024 PropMap::Builder& curPropMap) {
2025 PropMap::Builder::iterator prevIt = curPropMap.find(traitProp.m_name);
2027 if (prevIt == curPropMap.end()) {
2028 // New prop, go ahead and add it
2029 Prop prop = traitProp;
2030 prop.m_class = this; // set current class as the first declaring prop
2031 // private props' mangled names contain the class name, so regenerate them
2032 if (prop.m_attrs & AttrPrivate) {
2033 prop.m_mangledName = manglePropName(m_preClass->name(), prop.m_name,
2034 prop.m_attrs);
2036 curPropMap.add(prop.m_name, prop);
2037 m_declPropInit.push_back(traitPropVal);
2038 } else {
2039 // Redeclared prop, make sure it matches previous declarations
2040 Prop& prevProp = curPropMap[prevIt->second];
2041 TypedValue& prevPropVal = m_declPropInit[prevIt->second];
2042 if (prevProp.m_attrs != traitProp.m_attrs ||
2043 !compatibleTraitPropInit(prevPropVal, traitPropVal)) {
2044 raise_error("trait declaration of property '%s' is incompatible with "
2045 "previous declaration", traitProp.m_name->data());
2050 void Class::importTraitStaticProp(ClassPtr trait,
2051 SProp& traitProp,
2052 PropMap::Builder& curPropMap,
2053 SPropMap::Builder& curSPropMap) {
2054 // Check if prop already declared as non-static
2055 if (curPropMap.find(traitProp.m_name) != curPropMap.end()) {
2056 raise_error("trait declaration of property '%s' is incompatible with "
2057 "previous declaration", traitProp.m_name->data());
2060 SPropMap::Builder::iterator prevIt = curSPropMap.find(traitProp.m_name);
2061 if (prevIt == curSPropMap.end()) {
2062 // New prop, go ahead and add it
2063 SProp prop = traitProp;
2064 prop.m_class = this; // set current class as the first declaring prop
2065 curSPropMap.add(prop.m_name, prop);
2066 } else {
2067 // Redeclared prop, make sure it matches previous declaration
2068 SProp& prevProp = curSPropMap[prevIt->second];
2069 TypedValue prevPropVal;
2070 if (prevProp.m_class == this) {
2071 // If this static property was declared by this class, we can
2072 // get the initial value directly from m_val
2073 prevPropVal = prevProp.m_val;
2074 } else {
2075 // If this static property was declared in a parent class, m_val
2076 // will be KindOfUninit, and we'll need to consult the appropriate
2077 // parent class to get the initial value.
2078 prevPropVal = getStaticPropInitVal(prevProp);
2080 if (prevProp.m_attrs != traitProp.m_attrs ||
2081 !compatibleTraitPropInit(traitProp.m_val, prevPropVal)) {
2082 raise_error("trait declaration of property '%s' is incompatible with "
2083 "previous declaration", traitProp.m_name->data());
2085 prevProp.m_class = this;
2086 prevProp.m_val = prevPropVal;
2090 void Class::importTraitProps(PropMap::Builder& curPropMap,
2091 SPropMap::Builder& curSPropMap) {
2092 if (attrs() & AttrNoExpandTrait) return;
2093 for (size_t t = 0; t < m_usedTraits.size(); t++) {
2094 ClassPtr trait = m_usedTraits[t];
2096 // instance properties
2097 for (Slot p = 0; p < trait->m_declProperties.size(); p++) {
2098 Prop& traitProp = trait->m_declProperties[p];
2099 TypedValue& traitPropVal = trait->m_declPropInit[p];
2100 importTraitInstanceProp(trait, traitProp, traitPropVal,
2101 curPropMap);
2104 // static properties
2105 for (Slot p = 0; p < trait->m_staticProperties.size(); ++p) {
2106 SProp& traitProp = trait->m_staticProperties[p];
2107 importTraitStaticProp(trait, traitProp, curPropMap,
2108 curSPropMap);
2113 void Class::addTraitPropInitializers(bool staticProps) {
2114 if (attrs() & AttrNoExpandTrait) return;
2115 for (unsigned t = 0; t < m_usedTraits.size(); t++) {
2116 ClassPtr trait = m_usedTraits[t];
2117 InitVec& traitInitVec = staticProps ? trait->m_sinitVec : trait->m_pinitVec;
2118 InitVec& thisInitVec = staticProps ? m_sinitVec : m_pinitVec;
2119 // Insert trait's 86[ps]init into the current class, avoiding repetitions.
2120 for (unsigned m = 0; m < traitInitVec.size(); m++) {
2121 // Linear search, but these vectors shouldn't be big.
2122 if (find(thisInitVec.begin(), thisInitVec.end(), traitInitVec[m]) ==
2123 thisInitVec.end()) {
2124 thisInitVec.push_back(traitInitVec[m]);
2130 void Class::setInitializers() {
2131 if (m_parent.get() != NULL) {
2132 // Copy parent's 86pinit() vector, so that the 86pinit() methods can be
2133 // called in reverse order without any search/recursion during
2134 // initialization.
2135 m_pinitVec = m_parent->m_pinitVec;
2138 // This class only has a __[ps]init() method if it's needed. Append to the
2139 // vectors of __[ps]init() methods, so that reverse iteration of the vectors
2140 // runs this class's __[ps]init() first, in case multiple classes in the
2141 // hierarchy initialize the same property.
2142 const Func* meth86pinit = findSpecialMethod(this, sd86pinit);
2143 if (meth86pinit != NULL) {
2144 m_pinitVec.push_back(meth86pinit);
2146 addTraitPropInitializers(false);
2147 const Func* sinit = findSpecialMethod(this, sd86sinit);
2148 if (sinit) {
2149 m_sinitVec.push_back(sinit);
2151 addTraitPropInitializers(true);
2153 m_needInitialization = (m_pinitVec.size() > 0 ||
2154 m_staticProperties.size() > 0);
2156 // The __init__ method defined in the Exception class gets special treatment
2157 static StringData* sd__init__ = StringData::GetStaticString("__init__");
2158 static StringData* sd_exn = StringData::GetStaticString("Exception");
2159 const Func* einit = lookupMethod(sd__init__);
2160 m_callsCustomInstanceInit =
2161 (einit && einit->preClass()->name()->isame(sd_exn));
2164 // Checks if interface methods are OK:
2165 // - there's no requirement if this is a trait, interface, or abstract class
2166 // - a non-abstract class must implement all methods from interfaces it
2167 // declares to implement (either directly or indirectly), arity must be
2168 // compatible (at least as many parameters, additional parameters must have
2169 // defaults), and typehints must be compatible
2170 void Class::checkInterfaceMethods() {
2171 for (ClassSet::const_iterator it = m_allInterfaces.begin();
2172 it != m_allInterfaces.end(); it++) {
2173 const Class* iface = *it;
2175 for (size_t m = 0; m < iface->m_methods.size(); m++) {
2176 Func* imeth = iface->m_methods[m];
2177 const StringData* methName = imeth->name();
2179 // Skip special methods
2180 if (Func::isSpecial(methName)) continue;
2182 Func* meth = lookupMethod(methName);
2184 if (attrs() & (AttrTrait | AttrInterface | AttrAbstract)) {
2185 if (meth == NULL) {
2186 // Skip unimplemented method.
2187 continue;
2189 } else {
2190 // Verify that method is not abstract within concrete class.
2191 if (meth == NULL || (meth->attrs() & AttrAbstract)) {
2192 raise_error("Class %s contains abstract method (%s) and "
2193 "must therefore be declared abstract or implement "
2194 "the remaining methods", name()->data(),
2195 methName->data());
2198 bool ifaceStaticMethod = imeth->attrs() & AttrStatic;
2199 bool classStaticMethod = meth->attrs() & AttrStatic;
2200 if (classStaticMethod != ifaceStaticMethod) {
2201 raise_error("Cannot make %sstatic method %s::%s() %sstatic "
2202 "in class %s",
2203 ifaceStaticMethod ? "" : "non-",
2204 iface->m_preClass->name()->data(), methName->data(),
2205 classStaticMethod ? "" : "non-",
2206 m_preClass->name()->data());
2208 if ((imeth->attrs() & AttrPublic) &&
2209 !(meth->attrs() & AttrPublic)) {
2210 raise_error("Access level to %s::%s() must be public "
2211 "(as in interface %s)", m_preClass->name()->data(),
2212 methName->data(), iface->m_preClass->name()->data());
2214 meth->parametersCompat(m_preClass.get(), imeth);
2219 void Class::setInterfaces() {
2220 if (attrs() & AttrInterface) {
2221 m_allInterfaces.insert(this);
2223 if (m_parent.get() != NULL) {
2224 m_allInterfaces.insert(m_parent->m_allInterfaces.begin(),
2225 m_parent->m_allInterfaces.end());
2227 for (PreClass::InterfaceVec::const_iterator it =
2228 m_preClass->interfaces().begin();
2229 it != m_preClass->interfaces().end(); ++it) {
2230 ClassPtr cp = Unit::loadClass(*it);
2231 if (cp.get() == NULL) {
2232 raise_error("Undefined interface: %s", (*it)->data());
2234 if (!(cp->attrs() & AttrInterface)) {
2235 raise_error("%s cannot implement %s - it is not an interface",
2236 m_preClass->name()->data(), cp->name()->data());
2238 m_declInterfaces.push_back(cp);
2239 m_allInterfaces.insert(cp->m_allInterfaces.begin(),
2240 cp->m_allInterfaces.end());
2242 checkInterfaceMethods();
2245 void Class::setUsedTraits() {
2246 for (PreClass::UsedTraitVec::const_iterator
2247 it = m_preClass->usedTraits().begin();
2248 it != m_preClass->usedTraits().end(); it++) {
2249 ClassPtr classPtr = Unit::loadClass(*it);
2250 if (classPtr.get() == NULL) {
2251 raise_error("Trait '%s' not found", (*it)->data());
2253 if (!(classPtr->attrs() & AttrTrait)) {
2254 raise_error("%s cannot use %s - it is not a trait",
2255 m_preClass->name()->data(),
2256 classPtr->name()->data());
2258 m_usedTraits.push_back(classPtr);
2262 void Class::setClassVec() {
2263 if (m_classVecLen > 1) {
2264 assert(m_parent.get() != NULL);
2265 memcpy(m_classVec, m_parent.get()->m_classVec,
2266 (m_classVecLen-1) * sizeof(Class*));
2268 m_classVec[m_classVecLen-1] = this;
2271 void Class::setInstanceBits() {
2272 setInstanceBitsImpl<false>();
2274 void Class::setInstanceBitsAndParents() {
2275 setInstanceBitsImpl<true>();
2278 template<bool setParents>
2279 void Class::setInstanceBitsImpl() {
2280 // Bit 0 is reserved to indicate whether or not the rest of the bits
2281 // are initialized yet.
2282 if (m_instanceBits.test(0)) return;
2284 InstanceBits bits;
2285 bits.set(0);
2286 auto setBits = [&](ClassPtr& c) {
2287 if (setParents) c->setInstanceBitsAndParents();
2288 bits |= c->m_instanceBits;
2290 if (m_parent.get()) setBits(m_parent);
2291 std::for_each(m_declInterfaces.begin(), m_declInterfaces.end(), setBits);
2293 unsigned bit;
2294 if (mapGet(s_instanceBits, m_preClass->name(), &bit)) {
2295 bits.set(bit);
2297 m_instanceBits = bits;
2300 // Finds the base class defining the given method (NULL if none).
2301 // Note: for methods imported via traits, the base class is the one that
2302 // uses/imports the trait.
2303 Class* Class::findMethodBaseClass(const StringData* methName) {
2304 const Func* f = lookupMethod(methName);
2305 if (f == NULL) return NULL;
2306 return f->baseCls();
2309 void Class::getMethodNames(const Class* ctx, HphpArray* methods) const {
2310 Func* const* pcMethods = m_preClass->methods();
2311 for (size_t i = 0, sz = m_preClass->numMethods(); i < sz; i++) {
2312 Func* func = pcMethods[i];
2313 if (isdigit(func->name()->data()[0])) continue;
2314 if (!(func->attrs() & AttrPublic)) {
2315 if (!ctx) continue;
2316 if (ctx != this) {
2317 if (func->attrs() & AttrPrivate) continue;
2318 func = lookupMethod(func->name());
2319 if (!ctx->classof(func->baseCls()) &&
2320 !func->baseCls()->classof(ctx)) {
2321 continue;
2325 methods->nvSet(const_cast<StringData*>(func->name()),
2326 (TypedValue*)&true_varNR, false);
2328 if (m_parent.get()) m_parent.get()->getMethodNames(ctx, methods);
2329 for (int i = 0, sz = m_declInterfaces.size(); i < sz; i++) {
2330 m_declInterfaces[i].get()->getMethodNames(ctx, methods);
2334 // Returns true iff this class declared the given method.
2335 // For trait methods, the class declaring them is the one that uses/imports
2336 // the trait.
2337 bool Class::declaredMethod(const Func* method) {
2338 if (method->preClass()->attrs() & AttrTrait) {
2339 return findMethodBaseClass(method->name()) == this;
2341 return method->preClass() == m_preClass.get();
2344 void Class::getClassInfo(ClassInfoVM* ci) {
2345 assert(ci);
2347 // Miscellaneous.
2348 Attr clsAttrs = attrs();
2349 int attr = 0;
2350 if (clsAttrs & AttrInterface) attr |= ClassInfo::IsInterface;
2351 if (clsAttrs & AttrAbstract) attr |= ClassInfo::IsAbstract;
2352 if (clsAttrs & AttrFinal) attr |= ClassInfo::IsFinal;
2353 if (clsAttrs & AttrTrait) attr |= ClassInfo::IsTrait;
2354 if (attr == 0) attr = ClassInfo::IsNothing;
2355 ci->m_attribute = (ClassInfo::Attribute)attr;
2357 ci->m_name = m_preClass->name()->data();
2359 ci->m_file = m_preClass->unit()->filepath()->data();
2360 ci->m_line1 = m_preClass->line1();
2361 ci->m_line2 = m_preClass->line2();
2362 ci->m_docComment = (m_preClass->docComment() != NULL)
2363 ? m_preClass->docComment()->data() : "";
2365 // Parent class.
2366 if (m_parent.get()) {
2367 ci->m_parentClass = m_parent->name()->data();
2368 } else {
2369 ci->m_parentClass = "";
2372 // Interfaces.
2373 for (unsigned i = 0; i < m_declInterfaces.size(); i++) {
2374 ci->m_interfacesVec.push_back(
2375 m_declInterfaces[i]->name()->data());
2376 ci->m_interfaces.insert(
2377 m_declInterfaces[i]->name()->data());
2380 // Used traits.
2381 for (unsigned t = 0; t < m_usedTraits.size(); t++) {
2382 const char* traitName = m_usedTraits[t]->name()->data();
2383 ci->m_traitsVec.push_back(traitName);
2384 ci->m_traits.insert(traitName);
2387 // Trait aliases.
2388 for (unsigned a = 0; a < m_traitAliases.size(); a++) {
2389 ci->m_traitAliasesVec.push_back(std::pair<String, String>
2390 (m_traitAliases[a].first->data(),
2391 m_traitAliases[a].second->data()));
2394 #define SET_FUNCINFO_BODY \
2395 ClassInfo::MethodInfo *m = new ClassInfo::MethodInfo; \
2396 func->getFuncInfo(m); \
2397 ci->m_methods[func->name()->data()] = m; \
2398 ci->m_methodsVec.push_back(m);
2400 // Methods: in source order (from our PreClass), then traits.
2401 for (size_t i = 0; i < m_preClass->numMethods(); ++i) {
2402 const StringData* name = m_preClass->methods()[i]->name();
2403 // Filter out special methods
2404 if (isdigit(name->data()[0])) continue;
2405 Func* func = lookupMethod(m_preClass->methods()[i]->name());
2406 assert(func);
2407 assert(declaredMethod(func));
2408 SET_FUNCINFO_BODY;
2411 for (Slot i = m_traitsBeginIdx; i < m_traitsEndIdx; ++i) {
2412 Func* func = m_methods[i];
2413 assert(func);
2414 if (!isdigit(func->name()->data()[0])) {
2415 SET_FUNCINFO_BODY;
2418 #undef SET_FUNCINFO_BODY
2420 // Properties.
2421 for (Slot i = 0; i < m_declProperties.size(); ++i) {
2422 if (m_declProperties[i].m_class != this) continue;
2423 ClassInfo::PropertyInfo *pi = new ClassInfo::PropertyInfo;
2424 pi->owner = ci;
2425 pi->name = m_declProperties[i].m_name->data();
2426 Attr propAttrs = m_declProperties[i].m_attrs;
2427 attr = 0;
2428 if (propAttrs & AttrProtected) attr |= ClassInfo::IsProtected;
2429 if (propAttrs & AttrPrivate) attr |= ClassInfo::IsPrivate;
2430 if (attr == 0) attr |= ClassInfo::IsPublic;
2431 if (propAttrs & AttrStatic) attr |= ClassInfo::IsStatic;
2432 pi->attribute = (ClassInfo::Attribute)attr;
2433 pi->docComment = (m_declProperties[i].m_docComment != NULL)
2434 ? m_declProperties[i].m_docComment->data() : "";
2436 ci->m_properties[pi->name] = pi;
2437 ci->m_propertiesVec.push_back(pi);
2440 for (Slot i = 0; i < m_staticProperties.size(); ++i) {
2441 if (m_staticProperties[i].m_class != this) continue;
2442 ClassInfo::PropertyInfo *pi = new ClassInfo::PropertyInfo;
2443 pi->owner = ci;
2444 pi->name = m_staticProperties[i].m_name->data();
2445 Attr propAttrs = m_staticProperties[i].m_attrs;
2446 attr = 0;
2447 if (propAttrs & AttrProtected) attr |= ClassInfo::IsProtected;
2448 if (propAttrs & AttrPrivate) attr |= ClassInfo::IsPrivate;
2449 if (attr == 0) attr |= ClassInfo::IsPublic;
2450 if (propAttrs & AttrStatic) attr |= ClassInfo::IsStatic;
2451 pi->attribute = (ClassInfo::Attribute)attr;
2452 pi->docComment = (m_staticProperties[i].m_docComment != NULL)
2453 ? m_staticProperties[i].m_docComment->data() : "";
2455 ci->m_properties[pi->name] = pi;
2456 ci->m_propertiesVec.push_back(pi);
2459 // Constants.
2460 for (Slot i = 0; i < m_constants.size(); ++i) {
2461 // Only include constants declared on this class
2462 if (m_constants[i].m_class != this) continue;
2464 ClassInfo::ConstantInfo *ki = new ClassInfo::ConstantInfo;
2465 ki->name = m_constants[i].m_name->data();
2466 ki->valueLen = m_constants[i].m_phpCode->size();
2467 ki->valueText = m_constants[i].m_phpCode->data();
2468 ki->setValue(tvAsCVarRef(clsCnsGet(m_constants[i].m_name)));
2470 ci->m_constants[ki->name] = ki;
2471 ci->m_constantsVec.push_back(ki);
2475 Class::PropInitVec::~PropInitVec() {
2476 if (!m_smart) free(m_data);
2479 Class::PropInitVec::PropInitVec() : m_data(NULL), m_size(0), m_smart(false) {}
2481 Class::PropInitVec*
2482 Class::PropInitVec::allocInRequestArena(const PropInitVec& src) {
2483 ThreadInfo* info UNUSED = ThreadInfo::s_threadInfo.getNoCheck();
2484 PropInitVec* p = new (request_arena()) PropInitVec;
2485 p->m_size = src.size();
2486 p->m_data = new (request_arena()) TypedValue[src.size()];
2487 memcpy(p->m_data, src.m_data, src.size() * sizeof(*p->m_data));
2488 p->m_smart = true;
2489 return p;
2492 const Class::PropInitVec&
2493 Class::PropInitVec::operator=(const PropInitVec& piv) {
2494 assert(!m_smart);
2495 if (this != &piv) {
2496 unsigned sz = m_size = piv.size();
2497 if (sz) sz = Util::roundUpToPowerOfTwo(sz);
2498 free(m_data);
2499 m_data = (TypedValue*)malloc(sz * sizeof(*m_data));
2500 assert(m_data);
2501 memcpy(m_data, piv.m_data, piv.size() * sizeof(*m_data));
2503 return *this;
2506 void Class::PropInitVec::push_back(const TypedValue& v) {
2507 assert(!m_smart);
2509 * the allocated size is always the next power of two (or zero)
2510 * so we just need to reallocate when we hit a power of two
2512 if (!m_size || Util::isPowerOfTwo(m_size)) {
2513 unsigned size = m_size ? m_size * 2 : 1;
2514 m_data = (TypedValue*)realloc(m_data, size * sizeof(*m_data));
2515 assert(m_data);
2517 tvDup(&v, &m_data[m_size++]);
2520 using Transl::TargetCache::handleToRef;
2522 const Class::PropInitVec* Class::getPropData() const {
2523 if (m_propDataCache == (unsigned)-1) return NULL;
2524 return handleToRef<PropInitVec*>(m_propDataCache);
2527 void Class::initPropHandle() const {
2528 if (UNLIKELY(m_propDataCache == (unsigned)-1)) {
2529 const_cast<unsigned&>(m_propDataCache) =
2530 Transl::TargetCache::allocClassInitProp(name());
2534 void Class::initProps() const {
2535 setPropData(initPropsImpl());
2538 void Class::setPropData(PropInitVec* propData) const {
2539 assert(getPropData() == NULL);
2540 initPropHandle();
2541 handleToRef<PropInitVec*>(m_propDataCache) = propData;
2544 TypedValue* Class::getSPropData() const {
2545 if (m_propSDataCache == (unsigned)-1) return NULL;
2546 return handleToRef<TypedValue*>(m_propSDataCache);
2549 void Class::initSPropHandle() const {
2550 if (UNLIKELY(m_propSDataCache == (unsigned)-1)) {
2551 const_cast<unsigned&>(m_propSDataCache) =
2552 Transl::TargetCache::allocClassInitSProp(name());
2556 TypedValue* Class::initSProps() const {
2557 TypedValue* sprops = initSPropsImpl();
2558 setSPropData(sprops);
2559 return sprops;
2562 void Class::setSPropData(TypedValue* sPropData) const {
2563 assert(getSPropData() == NULL);
2564 initSPropHandle();
2565 handleToRef<TypedValue*>(m_propSDataCache) = sPropData;
2568 } } // HPHP::VM