in hphp/tools/type-info-gens/gen-type-scanners.cpp [1606:2142]
Generator::Action Generator::inferAction(const Object& object,
bool conservative_everything) const {
if (object.incomplete) {
throw Exception{
folly::sformat(
"Trying to infer actions on object type '{}' at ({},{}) "
"which is incomplete",
object.name.name,
object.key.object_id,
object.key.compile_unit_id
)
};
}
Action action;
if (conservative_everything) {
action.conservative_all = true;
action.conservative_all_bases = true;
return action;
}
// White-listing and forbidden templates are determined by just checking the
// name against explicit lists.
if (HPHP::type_scan::detail::isIgnoredType(object.name.name)) {
action.whitelisted = true;
return action;
}
if (HPHP::type_scan::detail::isForbiddenTemplate(object.name.name)) {
sanityCheckTemplateParams(object);
action.forbidden_template = true;
return action;
}
if (HPHP::type_scan::detail::isForcedConservativeTemplate(object.name.name)) {
sanityCheckTemplateParams(object);
action.conservative_all = true;
action.conservative_all_bases = true;
return action;
}
const auto find_member = [&](const std::string& field) {
return findMemberHelper(field, object);
};
const auto find_base = [&](const Object& base) {
return std::any_of(
object.bases.begin(),
object.bases.end(),
[&](const Object::Base& b) { return &getObject(b.type) == &base; }
);
};
for (const auto& fun : object.functions) {
// Sanity check special member function. All the functions should take a
// const pointer to the contained object type as the first parameter (the
// this pointer), and a non-const reference to HPHP::type_scan::Scanner as
// the second (and nothing else). The return type should be void.
auto verify_func = [&](const Object::Function& func) {
if (func.kind != Object::Function::Kind::k_member) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"which is not a non-static, non-virtual member",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name
)
};
}
if (!func.ret_type.isVoid()) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"which does not have a void return type",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name
)
};
}
if (func.arg_types.size() != 2) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"which does not take exactly two parameter ({})",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
func.arg_types.size()
)
};
}
const auto& this_arg = func.arg_types[0];
const auto* this_ptr_arg = this_arg.asPtr();
if (!this_ptr_arg) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose first parameter isn't a pointer type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
this_arg.toString()
)
};
}
const auto* this_const_arg = this_ptr_arg->pointee.asConst();
if (!this_const_arg) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose first parameter isn't a const pointer type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
this_arg.toString()
)
};
}
const auto* this_obj_arg = this_const_arg->modified.asObject();
if (!this_obj_arg) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose first parameter isn't a pointer type to object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
this_arg.toString()
)
};
}
if (&getObject(*this_obj_arg) != &object) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose first parameter isn't a valid this pointer '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
this_arg.toString()
)
};
}
const auto& scanner_arg = func.arg_types[1];
const auto* scanner_ref_arg = scanner_arg.asRef();
if (!scanner_ref_arg) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose second parameter isn't a reference '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
scanner_arg.toString()
)
};
}
const auto* scanner_obj_arg = scanner_ref_arg->referenced.asObject();
if (!scanner_obj_arg) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose second parameter isn't a reference to object-type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
scanner_arg.toString()
)
};
}
const auto& scanner_obj = getObject(*scanner_obj_arg);
if (scanner_obj.name.name != s_scanner_name) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains scanner func '{}' "
"whose second parameter isn't a reference to "
"{} '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
func.name,
std::string{s_scanner_name},
scanner_arg.toString()
)
};
}
};
// Custom scanner for particular field.
auto custom_field = splitFieldName(
fun.name,
HPHP::type_scan::detail::kCustomFieldName
);
if (!custom_field.empty()) {
verify_func(fun);
if (!find_member(custom_field)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains custom field marker "
"referring to unknown non-static field '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
custom_field
)
};
}
action.custom_fields.emplace(
std::move(custom_field),
fun.linkage_name
);
}
// Custom scanner for entire object.
if (fun.name == HPHP::type_scan::detail::kCustomName) {
verify_func(fun);
action.custom_all = fun.linkage_name;
continue;
}
// Custom scanner for base classes.
if (fun.name == HPHP::type_scan::detail::kCustomBasesScannerName) {
verify_func(fun);
action.custom_bases_scanner = fun.linkage_name;
continue;
}
}
for (const auto& member : object.members) {
// All special member markers should be static, so ignore anything that's
// not.
if (member.offset) continue;
// Ignore a field.
auto ignore_field = splitFieldName(
member.name,
HPHP::type_scan::detail::kIgnoreFieldName
);
if (!ignore_field.empty()) {
if (!find_member(ignore_field)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains ignore field marker "
"referring to unknown non-static field '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
ignore_field
)
};
}
action.ignore_fields.emplace(std::move(ignore_field));
continue;
}
// Scan field conservatively.
auto conservative_field = splitFieldName(
member.name,
HPHP::type_scan::detail::kConservativeFieldName
);
if (!conservative_field.empty()) {
if (!find_member(conservative_field)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains conservative field marker "
"referring to unknown non-static field '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
conservative_field
)
};
}
action.conservative_fields.emplace(std::move(conservative_field));
continue;
}
// Marks flexible array field. There can only be one of these per object
// type.
auto flexible_array_field = splitFieldName(
member.name,
HPHP::type_scan::detail::kFlexibleArrayFieldName
);
if (!flexible_array_field.empty()) {
if (!action.flexible_array_field.empty()) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains more than one flexible "
"array field marker",
object.name.name,
object.key.object_id,
object.key.compile_unit_id
)
};
}
if (!find_member(flexible_array_field)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains flexible array field marker "
"referring to unknown non-static field '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
flexible_array_field
)
};
}
action.flexible_array_field = std::move(flexible_array_field);
}
// Ignore entire object.
if (member.name == HPHP::type_scan::detail::kIgnoreName) {
action.ignore_all = true;
continue;
}
// Conservative scan entire object.
if (member.name == HPHP::type_scan::detail::kConservativeName) {
action.conservative_all = true;
continue;
}
// Ignore specific base.
if (member.name == HPHP::type_scan::detail::kIgnoreBaseName) {
const auto* ignore_type = stripModifiers(member.type).asObject();
if (!ignore_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains an ignore base marker "
"for a non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
member.type.toString()
)
};
}
const auto& ignore = getObject(*ignore_type);
// This is a variadic template, so sanity check it.
sanityCheckTemplateParams(ignore);
for (const auto& param : ignore.template_params) {
const auto* ignored_type = stripModifiers(param.type).asObject();
if (!ignored_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains an ignore base marker "
"instantiated on non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
param.type.toString()
)
};
}
const auto& ignored = getObject(*ignored_type);
if (!find_base(ignored)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains an ignore base marker "
"instantiated on object-type '{}' which isn't a base class",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
ignored.name.name
)
};
}
action.ignored_bases.emplace(&ignored);
}
continue;
}
// Don't complain about a particular base class violating a forbidden
// template check.
if (member.name == HPHP::type_scan::detail::kSilenceForbiddenBaseName) {
const auto* silence_type = stripModifiers(member.type).asObject();
if (!silence_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a silence base marker "
"for a non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
member.type.toString()
)
};
}
const auto& silence = getObject(*silence_type);
// This is a variadic template, so sanity check it.
sanityCheckTemplateParams(silence);
for (const auto& param : silence.template_params) {
const auto* silenced_type = stripModifiers(param.type).asObject();
if (!silenced_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a silence base marker "
"instantiated on non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
param.type.toString()
)
};
}
const auto& silenced = getObject(*silenced_type);
if (!find_base(silenced)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a silence base marker "
"instantiated on object-type '{}' which isn't a base class",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
silenced.name.name
)
};
}
action.silenced_bases.emplace(&silenced);
}
continue;
}
// List of base classes to apply the custom bases scan to.
if (action.custom_bases_scanner &&
member.name == HPHP::type_scan::detail::kCustomBasesName) {
const auto* custom_list_type = stripModifiers(member.type).asObject();
if (!custom_list_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a custom base marker "
"for a non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
member.type.toString()
)
};
}
const auto& custom_list = getObject(*custom_list_type);
// This is a variadic template, so sanity check it.
sanityCheckTemplateParams(custom_list);
for (const auto& param : custom_list.template_params) {
const auto* custom_type = stripModifiers(param.type).asObject();
if (!custom_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a custom base marker "
"instantiated on non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
param.type.toString()
)
};
}
const auto& custom = getObject(*custom_type);
if (!find_base(custom)) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a custom base marker "
"instantiated on object-type '{}' which isn't a base class",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
custom.name.name
)
};
}
action.custom_bases.emplace(&custom);
}
continue;
}
// If there's a custom scanner for the entire object, list of types to guard
// on.
if (action.custom_all &&
member.name == HPHP::type_scan::detail::kCustomGuardName) {
const auto* guard_type = stripModifiers(member.type).asObject();
if (!guard_type) {
throw Exception{
folly::sformat(
"Object type '{}' at ({},{}) contains a custom guard marker "
"instantiated on non-object type '{}'",
object.name.name,
object.key.object_id,
object.key.compile_unit_id,
member.type.toString()
)
};
}
const auto& guard = getObject(*guard_type);
// This is a variadic template, so sanity check it.
sanityCheckTemplateParams(guard);
for (const auto& param : guard.template_params) {
action.custom_guards.emplace(¶m.type);
}
continue;
}
}
return action;
}