open_toontown_panda3d/direct/src/dcparser/dcClass.cxx

645 lines
16 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file dcClass.cxx
* @author drose
* @date 2000-10-05
*/
#include "dcClass.h"
#include "dcFile.h"
#include "dcAtomicField.h"
#include "hashGenerator.h"
#include "dcindent.h"
#include "dcClassParameter.h"
#include <algorithm>
using std::string;
#ifdef WITHIN_PANDA
#ifndef CPPPARSER
PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update");
PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate");
#endif // CPPPARSER
ConfigVariableBool dc_multiple_inheritance
("dc-multiple-inheritance", true,
PRC_DESC("Set this true to support multiple inheritance in the dc file. "
"If this is false, the old way, multiple inheritance is not "
"supported, but field numbers will be numbered sequentially, "
"which may be required to support old code that assumed this."));
ConfigVariableBool dc_virtual_inheritance
("dc-virtual-inheritance", true,
PRC_DESC("Set this true to support proper virtual inheritance in the "
"dc file, so that diamond-of-death type constructs can be used. "
"This also enables shadowing (overloading) of inherited method "
"names from a base class."));
ConfigVariableBool dc_sort_inheritance_by_file
("dc-sort-inheritance-by-file", true,
PRC_DESC("This is a temporary hack. This should be true if you are using "
"version 1.42 of the otp_server.exe binary, which sorted inherited "
"fields based on the order of the classes within the DC file, "
"rather than based on the order in which the references are made "
"within the class."));
#endif // WITHIN_PANDA
class SortFieldsByIndex {
public:
inline bool operator ()(const DCField *a, const DCField *b) const {
return a->get_number() < b->get_number();
}
};
/**
*
*/
DCClass::
DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) :
#ifdef WITHIN_PANDA
_class_update_pcollector(_update_pcollector, name),
_class_generate_pcollector(_generate_pcollector, name),
#endif
_dc_file(dc_file),
_name(name),
_is_struct(is_struct),
_bogus_class(bogus_class)
{
_number = -1;
_constructor = nullptr;
#ifdef WITHIN_PANDA
_python_class_defs = nullptr;
#endif
}
/**
*
*/
DCClass::
~DCClass() {
delete _constructor;
Fields::iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
delete (*fi);
}
}
/**
*
*/
DCClass *DCClass::
as_class() {
return this;
}
/**
*
*/
const DCClass *DCClass::
as_class() const {
return this;
}
/**
* Returns the number of base classes this class inherits from.
*/
int DCClass::
get_num_parents() const {
return _parents.size();
}
/**
* Returns the nth parent class this class inherits from.
*/
DCClass *DCClass::
get_parent(int n) const {
nassertr(n >= 0 && n < (int)_parents.size(), nullptr);
return _parents[n];
}
/**
* Returns true if this class has a constructor method, false if it just uses
* the default constructor.
*/
bool DCClass::
has_constructor() const {
return (_constructor != nullptr);
}
/**
* Returns the constructor method for this class if it is defined, or NULL if
* the class uses the default constructor.
*/
DCField *DCClass::
get_constructor() const {
return _constructor;
}
/**
* Returns the number of fields defined directly in this class, ignoring
* inheritance.
*/
int DCClass::
get_num_fields() const {
return _fields.size();
}
/**
* Returns the nth field in the class. This is not necessarily the field with
* index n; this is the nth field defined in the class directly, ignoring
* inheritance.
*/
DCField *DCClass::
get_field(int n) const {
#ifndef NDEBUG //[
if (n < 0 || n >= (int)_fields.size()) {
std::cerr << *this << " "
<< "n:" << n << " _fields.size():"
<< (int)_fields.size() << std::endl;
// __asm { int 3 }
}
#endif //]
nassertr_always(n >= 0 && n < (int)_fields.size(), nullptr);
return _fields[n];
}
/**
* Returns a pointer to the DCField that shares the indicated name. If the
* named field is not found in the current class, the parent classes will be
* searched, so the value returned may not actually be a field within this
* class. Returns NULL if there is no such field defined.
*/
DCField *DCClass::
get_field_by_name(const string &name) const {
FieldsByName::const_iterator ni;
ni = _fields_by_name.find(name);
if (ni != _fields_by_name.end()) {
return (*ni).second;
}
// We didn't have such a field, so check our parents.
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
DCField *result = (*pi)->get_field_by_name(name);
if (result != nullptr) {
return result;
}
}
// Nobody knew what this field is.
return nullptr;
}
/**
* Returns a pointer to the DCField that has the indicated index number. If
* the numbered field is not found in the current class, the parent classes
* will be searched, so the value returned may not actually be a field within
* this class. Returns NULL if there is no such field defined.
*/
DCField *DCClass::
get_field_by_index(int index_number) const {
FieldsByIndex::const_iterator ni;
ni = _fields_by_index.find(index_number);
if (ni != _fields_by_index.end()) {
return (*ni).second;
}
// We didn't have such a field, so check our parents.
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
DCField *result = (*pi)->get_field_by_index(index_number);
if (result != nullptr) {
// Cache this result for future lookups.
((DCClass *)this)->_fields_by_index[index_number] = result;
return result;
}
}
// Nobody knew what this field is.
return nullptr;
}
/**
* Returns the total number of field fields defined in this class and all
* ancestor classes.
*/
int DCClass::
get_num_inherited_fields() const {
if (dc_multiple_inheritance && dc_virtual_inheritance &&
_dc_file != nullptr) {
_dc_file->check_inherited_fields();
if (_inherited_fields.empty()) {
((DCClass *)this)->rebuild_inherited_fields();
}
// This assertion causes trouble when we are only parsing an incomplete DC
// file. nassertr(is_bogus_class() || !_inherited_fields.empty(), 0);
return (int)_inherited_fields.size();
} else {
int num_fields = get_num_fields();
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
num_fields += (*pi)->get_num_inherited_fields();
}
return num_fields;
}
}
/**
* Returns the nth field field in the class and all of its ancestors.
*
* This *used* to be the same thing as get_field_by_index(), back when the
* fields were numbered sequentially within a class's inheritance hierarchy.
* Now that fields have a globally unique index number, this is no longer
* true.
*/
DCField *DCClass::
get_inherited_field(int n) const {
if (dc_multiple_inheritance && dc_virtual_inheritance &&
_dc_file != nullptr) {
_dc_file->check_inherited_fields();
if (_inherited_fields.empty()) {
((DCClass *)this)->rebuild_inherited_fields();
}
nassertr(n >= 0 && n < (int)_inherited_fields.size(), nullptr);
return _inherited_fields[n];
} else {
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
int psize = (*pi)->get_num_inherited_fields();
if (n < psize) {
return (*pi)->get_inherited_field(n);
}
n -= psize;
}
return get_field(n);
}
}
/**
* Returns true if this class, or any class in the inheritance heirarchy for
* this class, is a "bogus" class--a forward reference to an as-yet-undefined
* class.
*/
bool DCClass::
inherits_from_bogus_class() const {
if (is_bogus_class()) {
return true;
}
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
if ((*pi)->inherits_from_bogus_class()) {
return true;
}
}
return false;
}
/**
* Write a string representation of this instance to <out>.
*/
void DCClass::
output(std::ostream &out) const {
if (_is_struct) {
out << "struct";
} else {
out << "dclass";
}
if (!_name.empty()) {
out << " " << _name;
}
}
/**
* Write a string representation of this instance to <out>.
*/
void DCClass::
output(std::ostream &out, bool brief) const {
output_instance(out, brief, "", "", "");
}
/**
* Generates a parseable description of the object to the indicated output
* stream.
*/
void DCClass::
write(std::ostream &out, bool brief, int indent_level) const {
indent(out, indent_level);
if (_is_struct) {
out << "struct";
} else {
out << "dclass";
}
if (!_name.empty()) {
out << " " << _name;
}
if (!_parents.empty()) {
Parents::const_iterator pi = _parents.begin();
out << " : " << (*pi)->_name;
++pi;
while (pi != _parents.end()) {
out << ", " << (*pi)->_name;
++pi;
}
}
out << " {";
if (!brief && _number >= 0) {
out << " // index " << _number;
}
out << "\n";
if (_constructor != nullptr) {
_constructor->write(out, brief, indent_level + 2);
}
Fields::const_iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
if (!(*fi)->is_bogus_field()) {
(*fi)->write(out, brief, indent_level + 2);
/*
if (true || (*fi)->has_default_value()) {
indent(out, indent_level + 2) << "// = ";
DCPacker packer;
packer.set_unpack_data((*fi)->get_default_value());
packer.begin_unpack(*fi);
packer.unpack_and_format(out, false);
if (!packer.end_unpack()) {
out << "<error>";
}
out << "\n";
}
*/
}
}
indent(out, indent_level) << "};\n";
}
/**
* Generates a parseable description of the object to the indicated output
* stream.
*/
void DCClass::
output_instance(std::ostream &out, bool brief, const string &prename,
const string &name, const string &postname) const {
if (_is_struct) {
out << "struct";
} else {
out << "dclass";
}
if (!_name.empty()) {
out << " " << _name;
}
if (!_parents.empty()) {
Parents::const_iterator pi = _parents.begin();
out << " : " << (*pi)->_name;
++pi;
while (pi != _parents.end()) {
out << ", " << (*pi)->_name;
++pi;
}
}
out << " {";
if (_constructor != nullptr) {
_constructor->output(out, brief);
out << "; ";
}
Fields::const_iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
if (!(*fi)->is_bogus_field()) {
(*fi)->output(out, brief);
out << "; ";
}
}
out << "}";
if (!prename.empty() || !name.empty() || !postname.empty()) {
out << " " << prename << name << postname;
}
}
/**
* Accumulates the properties of this class into the hash.
*/
void DCClass::
generate_hash(HashGenerator &hashgen) const {
hashgen.add_string(_name);
if (is_struct()) {
hashgen.add_int(1);
}
hashgen.add_int(_parents.size());
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
hashgen.add_int((*pi)->get_number());
}
if (_constructor != nullptr) {
_constructor->generate_hash(hashgen);
}
hashgen.add_int(_fields.size());
Fields::const_iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
(*fi)->generate_hash(hashgen);
}
}
/**
* Empties the list of inherited fields for the class, so that it may be
* rebuilt. This is normally only called by
* DCFile::rebuild_inherited_fields().
*/
void DCClass::
clear_inherited_fields() {
_inherited_fields.clear();
}
/**
* Recomputes the list of inherited fields for the class.
*/
void DCClass::
rebuild_inherited_fields() {
typedef pset<string> Names;
Names names;
_inherited_fields.clear();
// First, all of the inherited fields from our parent are at the top of the
// list.
Parents::const_iterator pi;
for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
const DCClass *parent = (*pi);
int num_inherited_fields = parent->get_num_inherited_fields();
for (int i = 0; i < num_inherited_fields; ++i) {
DCField *field = parent->get_inherited_field(i);
if (field->get_name().empty()) {
// Unnamed fields are always inherited. Except in the hack case.
if (!dc_sort_inheritance_by_file) {
_inherited_fields.push_back(field);
}
} else {
bool inserted = names.insert(field->get_name()).second;
if (inserted) {
// The earlier parent shadows the later parent.
_inherited_fields.push_back(field);
}
}
}
}
// Now add the local fields at the end of the list. If any fields in this
// list were already defined by a parent, we will shadow the parent
// definition (that is, remove the parent's field from our list of inherited
// fields).
Fields::const_iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
DCField *field = (*fi);
if (field->get_name().empty()) {
// Unnamed fields are always added.
_inherited_fields.push_back(field);
} else {
bool inserted = names.insert(field->get_name()).second;
if (!inserted) {
// This local field shadows an inherited field. Remove the parent's
// field from our list.
shadow_inherited_field(field->get_name());
}
// Now add the local field.
_inherited_fields.push_back(field);
}
}
if (dc_sort_inheritance_by_file) {
// Temporary hack.
sort(_inherited_fields.begin(), _inherited_fields.end(), SortFieldsByIndex());
}
}
/**
* This is called only by rebuild_inherited_fields(). It removes the named
* field from the list of _inherited_fields, presumably in preparation for
* adding a new definition below.
*/
void DCClass::
shadow_inherited_field(const string &name) {
Fields::iterator fi;
for (fi = _inherited_fields.begin(); fi != _inherited_fields.end(); ++fi) {
DCField *field = (*fi);
if (field->get_name() == name) {
_inherited_fields.erase(fi);
return;
}
}
// If we get here, the named field wasn't in the list. Huh.
nassert_raise("named field not in list");
}
/**
* Adds the newly-allocated field to the class. The class becomes the owner
* of the pointer and will delete it when it destructs. Returns true if the
* field is successfully added, or false if there was a name conflict or some
* other problem.
*/
bool DCClass::
add_field(DCField *field) {
nassertr(field->get_class() == this || field->get_class() == nullptr, false);
field->set_class(this);
if (_dc_file != nullptr) {
_dc_file->mark_inherited_fields_stale();
}
if (!field->get_name().empty()) {
if (field->get_name() == _name) {
// This field is a constructor.
if (_constructor != nullptr) {
// We already have a constructor.
return false;
}
if (field->as_atomic_field() == nullptr) {
// The constructor must be an atomic field.
return false;
}
_constructor = field;
_fields_by_name.insert
(FieldsByName::value_type(field->get_name(), field));
return true;
}
bool inserted = _fields_by_name.insert
(FieldsByName::value_type(field->get_name(), field)).second;
if (!inserted) {
return false;
}
}
if (_dc_file != nullptr &&
((dc_virtual_inheritance && dc_sort_inheritance_by_file) || !is_struct())) {
if (dc_multiple_inheritance) {
_dc_file->set_new_index_number(field);
} else {
field->set_number(get_num_inherited_fields());
}
bool inserted = _fields_by_index.insert
(FieldsByIndex::value_type(field->get_number(), field)).second;
// It shouldn't be possible for that to fail.
nassertr(inserted, false);
}
_fields.push_back(field);
return true;
}
/**
* Adds a new parent to the inheritance hierarchy of the class. This is
* normally called only during parsing.
*/
void DCClass::
add_parent(DCClass *parent) {
_parents.push_back(parent);
_dc_file->mark_inherited_fields_stale();
}
/**
* Assigns the unique number to this class. This is normally called only by
* the DCFile interface as the class is added.
*/
void DCClass::
set_number(int number) {
_number = number;
}