in hphp/runtime/vm/class.cpp [2841:3219]
void Class::setProperties() {
int numInaccessible = 0;
PropMap::Builder curPropMap;
SPropMap::Builder curSPropMap;
m_hasDeepInitProps = false;
std::vector<uint16_t> slotIndex;
if (m_parent.get() != nullptr) {
// m_hasDeepInitProps indicates if there are properties that require
// deep initialization. Note there are cases where m_hasDeepInitProps is
// true but none of the properties require deep initialization; this can
// happen if a derived class redeclares a public or protected property
// from an ancestor class. We still get correct behavior in these cases,
// so it works out okay.
m_hasDeepInitProps = m_parent->m_hasDeepInitProps;
for (auto const& parentProp : m_parent->declProperties()) {
// Copy parent's declared property. Protected properties may be
// weakened to public below, but otherwise, the parent's properties
// will stay the same for this class.
Prop prop;
prop.preProp = parentProp.preProp;
prop.cls = parentProp.cls;
prop.baseCls = parentProp.baseCls;
prop.mangledName = parentProp.mangledName;
prop.attrs = parentProp.attrs | AttrNoBadRedeclare;
prop.typeConstraint = parentProp.typeConstraint;
prop.ubs = parentProp.ubs;
prop.name = parentProp.name;
prop.repoAuthType = parentProp.repoAuthType;
if (!(parentProp.attrs & AttrPrivate)) {
curPropMap.add(prop.name, prop);
} else {
++numInaccessible;
curPropMap.addUnnamed(prop);
}
}
m_declPropInit = m_parent->m_declPropInit;
m_needsPropInitialCheck = m_parent->m_needsPropInitialCheck;
auto& parentSlotIndex = m_parent->m_slotIndex;
slotIndex.insert(slotIndex.end(),
parentSlotIndex.begin(), parentSlotIndex.end());
for (auto const& parentProp : m_parent->staticProperties()) {
if ((parentProp.attrs & AttrPrivate) &&
!(parentProp.attrs & AttrLSB)) continue;
// Alias parent's static property.
SProp sProp;
sProp.preProp = parentProp.preProp;
sProp.name = parentProp.name;
sProp.attrs = parentProp.attrs | AttrNoBadRedeclare;
sProp.typeConstraint = parentProp.typeConstraint;
sProp.ubs = parentProp.ubs;
sProp.cls = parentProp.cls;
sProp.repoAuthType = parentProp.repoAuthType;
tvWriteUninit(sProp.val);
curSPropMap.add(sProp.name, sProp);
}
}
Slot traitIdx = m_preClass->numProperties();
if (m_preClass->attrs() & AttrNoExpandTrait) {
while (traitIdx &&
(m_preClass->properties()[traitIdx - 1].attrs() & AttrTrait)) {
traitIdx--;
}
}
Slot serializationIdx = 0;
std::vector<bool> serializationVisited(curPropMap.size(), false);
Slot staticSerializationIdx = 0;
std::vector<bool> staticSerializationVisited(curSPropMap.size(), false);
static_assert(AttrPublic < AttrProtected && AttrProtected < AttrPrivate, "");
auto const firstOwnPropSlot = curPropMap.size();
for (Slot slot = 0; slot < traitIdx; ++slot) {
const PreClass::Prop* preProp = &m_preClass->properties()[slot];
if (!(preProp->attrs() & AttrStatic)) {
// Overlay/append this class's protected and public properties onto/to
// those of the parent, and append this class's private properties.
// Append order doesn't matter here (unlike in setMethods()).
// Prohibit static-->non-static redeclaration.
SPropMap::Builder::iterator it5 = curSPropMap.find(preProp->name());
if (it5 != curSPropMap.end()) {
raise_error("Cannot redeclare static %s::$%s as non-static %s::$%s",
curSPropMap[it5->second].cls->name()->data(),
preProp->name()->data(), m_preClass->name()->data(),
preProp->name()->data());
}
// Get parent's equivalent property, if one exists.
const Prop* parentProp = nullptr;
if (m_parent.get() != nullptr) {
Slot id = m_parent->m_declProperties.findIndex(preProp->name());
if (id != kInvalidSlot) {
parentProp = &m_parent->m_declProperties[id];
}
}
// Prohibit strengthening.
if (parentProp
&& (preProp->attrs() & VisibilityAttrs)
> (parentProp->attrs & VisibilityAttrs)) {
raise_error(
"Access level to %s::$%s must be %s (as in class %s) or weaker",
m_preClass->name()->data(), preProp->name()->data(),
attrToVisibilityStr(parentProp->attrs),
m_parent->name()->data());
}
if (preProp->attrs() & AttrDeepInit) {
m_hasDeepInitProps = true;
}
auto addNewProp = [&] {
Prop prop;
initProp(prop, preProp);
curPropMap.add(preProp->name(), prop);
curPropMap[serializationIdx++].serializationIdx = curPropMap.size() - 1;
serializationVisited.push_back(true);
m_declPropInit.push_back(preProp->val());
slotIndex.push_back(slotIndex.size());
};
auto const lateInitCheck = [&] (const Class::Prop& prop) {
if ((prop.attrs ^ preProp->attrs()) & AttrLateInit) {
raise_error(
"Property %s::$%s must %sbe <<__LateInit>> (as in class %s)",
m_preClass->name()->data(),
preProp->name()->data(),
prop.attrs & AttrLateInit ? "" : "not ",
m_parent->name()->data()
);
}
};
auto const redeclareProp = [&] (const Slot slot) {
// For duplicate property name, add the slot # defined in curPropMap,
// and mark the property visited.
assertx(serializationVisited.size() > slot);
if (!serializationVisited[slot]) {
curPropMap[serializationIdx++].serializationIdx = slot;
serializationVisited[slot] = true;
}
auto& prop = curPropMap[slot];
assertx((preProp->attrs() & VisibilityAttrs)
<= (prop.attrs & VisibilityAttrs));
assertx(!(prop.attrs & AttrNoImplicitNullable) ||
(preProp->attrs() & AttrNoImplicitNullable));
assertx(prop.attrs & AttrNoBadRedeclare);
if ((preProp->attrs() & AttrIsConst) != (prop.attrs & AttrIsConst)) {
raise_error("Cannot redeclare property %s of class %s with different "
"constness in class %s", preProp->name()->data(),
m_parent->name()->data(), m_preClass->name()->data());
}
lateInitCheck(prop);
prop.preProp = preProp;
prop.cls = this;
if ((preProp->attrs() & VisibilityAttrs)
< (prop.attrs & VisibilityAttrs)) {
assertx((prop.attrs & VisibilityAttrs) == AttrProtected);
assertx((preProp->attrs() & VisibilityAttrs) == AttrPublic);
// Weaken protected property to public.
prop.mangledName = preProp->mangledName();
prop.attrs = Attr(prop.attrs ^ (AttrProtected|AttrPublic));
}
auto const& tc = preProp->typeConstraint();
if (RuntimeOption::EvalCheckPropTypeHints > 0 &&
!(preProp->attrs() & AttrNoBadRedeclare) &&
(tc.maybeInequivalentForProp(prop.typeConstraint) ||
!preProp->upperBounds().empty() ||
!prop.ubs.empty())) {
// If this property isn't obviously not redeclaring a property in
// the parent, we need to check that when we initialize the class.
prop.attrs = Attr(prop.attrs & ~AttrNoBadRedeclare);
m_selfMaybeRedefsPropTy = true;
m_maybeRedefsPropTy = true;
}
prop.typeConstraint = tc;
prop.ubs = preProp->upperBounds();
prop.repoAuthType = preProp->repoAuthType();
if (preProp->attrs() & AttrNoImplicitNullable) {
prop.attrs |= AttrNoImplicitNullable;
}
attrSetter(prop.attrs,
preProp->attrs() & AttrSystemInitialValue,
AttrSystemInitialValue);
attrSetter(prop.attrs,
preProp->attrs() & AttrInitialSatisfiesTC,
AttrInitialSatisfiesTC);
attrSetter(prop.attrs,
preProp->attrs() & AttrPersistent,
AttrPersistent);
checkPrePropVal(prop, preProp);
auto index = slotIndex[slot];
tvCopy(preProp->val(), m_declPropInit[index].val);
copyDeepInitAttr(preProp, &prop);
};
switch (preProp->attrs() & VisibilityAttrs) {
case AttrPrivate: {
addNewProp();
break;
}
case AttrProtected: {
// Check whether a superclass has already declared this protected
// property.
PropMap::Builder::iterator it2 = curPropMap.find(preProp->name());
if (it2 == curPropMap.end()) {
addNewProp();
} else {
redeclareProp(it2->second);
}
break;
}
case AttrPublic: {
// Check whether a superclass has already declared this as a
// protected/public property.
auto it2 = curPropMap.find(preProp->name());
if (it2 == curPropMap.end()) {
addNewProp();
} else {
redeclareProp(it2->second);
}
break;
}
default: always_assert(false);
}
} else { // Static property.
// Prohibit non-static-->static redeclaration.
auto const it2 = curPropMap.find(preProp->name());
if (it2 != curPropMap.end()) {
auto& prop = curPropMap[it2->second];
raise_error("Cannot redeclare non-static %s::$%s as static %s::$%s",
prop.cls->name()->data(),
preProp->name()->data(),
m_preClass->name()->data(),
preProp->name()->data());
}
// Get parent's equivalent property, if one exists.
auto const it3 = curSPropMap.find(preProp->name());
Slot sPropInd = kInvalidSlot;
// Prohibit strengthening.
if (it3 != curSPropMap.end()) {
const SProp& parentSProp = curSPropMap[it3->second];
if ((preProp->attrs() & VisibilityAttrs)
> (parentSProp.attrs & VisibilityAttrs)) {
raise_error(
"Access level to %s::$%s must be %s (as in class %s) or weaker",
m_preClass->name()->data(), preProp->name()->data(),
attrToVisibilityStr(parentSProp.attrs),
m_parent->name()->data());
}
// Prohibit overlaying LSB static properties.
if (parentSProp.attrs & AttrLSB) {
raise_error(
"Cannot redeclare LSB static %s::%s as %s::%s",
parentSProp.cls->name()->data(),
preProp->name()->data(),
m_preClass->name()->data(),
preProp->name()->data());
}
sPropInd = it3->second;
}
if (sPropInd == kInvalidSlot) {
SProp sProp;
initProp(sProp, preProp);
curSPropMap.add(sProp.name, sProp);
curSPropMap[staticSerializationIdx++].serializationIdx =
curSPropMap.size() - 1;
staticSerializationVisited.push_back(true);
} else {
// Overlay ancestor's property.
auto& sProp = curSPropMap[sPropInd];
initProp(sProp, preProp);
assertx(staticSerializationVisited.size() > sPropInd);
if (!staticSerializationVisited[sPropInd]) {
staticSerializationVisited[sPropInd] = true;
curSPropMap[staticSerializationIdx++].serializationIdx = sPropInd;
}
}
}
}
importTraitProps(traitIdx, curPropMap, curSPropMap, slotIndex,
serializationIdx, serializationVisited,
staticSerializationIdx, staticSerializationVisited);
auto const pastOwnPropSlot = curPropMap.size();
sortOwnProps(curPropMap, firstOwnPropSlot, pastOwnPropSlot, slotIndex);
assertx(slotIndex.size() == curPropMap.size());
// set serialization index for parent properties at the end
if (m_parent.get() != nullptr) {
for (Slot i = 0; i < m_parent->declProperties().size(); ++i) {
// For non-static properties, slot is always equal to parentSlot
Slot slot = m_parent->declProperties()[i].serializationIdx;
assertx(serializationVisited.size() > slot);
if (!serializationVisited[slot]) {
curPropMap[serializationIdx++].serializationIdx = slot;
}
}
for (Slot i = 0; i < m_parent->staticProperties().size(); ++i) {
Slot parentSlot = m_parent->staticProperties()[i].serializationIdx;
auto const& prop = m_parent->staticProperties()[parentSlot];
if ((prop.attrs & AttrPrivate) && !(prop.attrs & AttrLSB)) continue;
auto it = curSPropMap.find(prop.name);
assertx(it != curSPropMap.end());
Slot slot = it->second;
assertx(staticSerializationVisited.size() > slot);
if (!staticSerializationVisited[slot]) {
curSPropMap[staticSerializationIdx++].serializationIdx = slot;
}
}
assertx(serializationIdx == curPropMap.size());
assertx(staticSerializationIdx == curSPropMap.size());
}
// LSB static properties that were inherited must be initialized separately.
for (Slot slot = 0; slot < curSPropMap.size(); ++slot) {
auto& sProp = curSPropMap[slot];
if ((sProp.attrs & AttrLSB) && sProp.cls != this) {
auto const& prevSProps = sProp.cls->m_staticProperties;
auto prevInd = prevSProps.findIndex(sProp.name);
assertx(prevInd != kInvalidSlot);
sProp.val = prevSProps[prevInd].val;
}
}
m_declProperties.create(curPropMap);
m_staticProperties.create(curSPropMap);
std::vector<ObjectProps::quick_index> slotQuickIndex;
slotQuickIndex.reserve(slotIndex.size());
for (auto i : slotIndex) {
slotQuickIndex.push_back(ObjectProps::quickIndex(i));
}
m_slotIndex = slotQuickIndex;
setupSProps();
m_declPropNumAccessible = m_declProperties.size() - numInaccessible;
// To speed up ObjectData::release, we only iterate over property indices
// up to the last countable property index. Here, "index" refers to the
// position of the property in memory, and "slot" to its logical slot.
uint16_t countablePropsEnd = 0;
for (Slot slot = 0; slot < m_declProperties.size(); ++slot) {
if (jit::irgen::propertyMayBeCountable(m_declProperties[slot])) {
auto const index =
safe_cast<uint16_t>(propSlotToIndex(slot) + uint16_t{1});
countablePropsEnd = std::max(countablePropsEnd, index);
}
}
assertx(m_declProperties.size() <= ObjectProps::max_index + 1);
assertx(countablePropsEnd <= ObjectProps::max_index);
m_countablePropsEnd = ObjectProps::quickIndex(countablePropsEnd);
FTRACE(3, "numDeclProperties = {}\n", m_declProperties.size());
FTRACE(3, "countablePropsEnd = {}\n", countablePropsEnd);
}