📄 class.cpp
字号:
/* CLASS.CPP
* The type Class
* UnderC C++ interpreter
* Steve Donovan, 2001
* This is GPL'd software, and the usual disclaimers apply.
* See LICENCE
*/
#pragma warning (disable:4786)
#define IN_TABLE_CPP
#include "common.h"
#include "directcall.h"
#include "operators.h"
#include "opcodes.h"
#include "tparser.h"
#include "std_utils.h"
#include "module.h"
#include "input.h"
void error(string msg); // shd be in common...
// in main.cpp
int uc_eval(char *expr, bool append_semicolon, bool synchronous, char *name, int lineno);
namespace {// private utility stuff
bool grab_list(const TypeList& tl1, TypeList& tl2);
typedef std::map<PEntry,PExprList> Entry2ExprMap;
FunctionContext *mFe;
PEntry begin_method(string name, Type rtype, char *arg_name, Type atype, int ftype);
void declare_method(string name, Type rtype, char *arg_name, Type atype, int ftype);
void end_method();
Function *get_std_match(const TypeList& tl, const FunctionList& fl, Type t);
PFBlock mVMT[MAX_VMT_ENTRIES];
Entry2ExprMap s_entry_map;
char *TRY_BLOCK_HANDLER_CLASS = "_tbhc_";
};
void *mVtable[MAX_VMT_ENTRIES];
// HACK ALERT!
#define m_base_ctor m_finalized
Class::Class(Table *parent,int access)
: NamedTable(parent,OREL,0),m_base_access(access),m_simple_struct(true),
m_default_constructor(NULL),m_copy_constructor(NULL),m_destructor(NULL),
m_has_constructors(false),m_base_entry(NULL),m_templ_info(NULL),
m_vtable_size(0),m_is_union(false),
m_abstract(false),m_base_ctor(false), m_import(NULL),m_imported(false),m_struct(false)
{
m_type = IS_STRUCT;
m_VMT = NULL;
set_base_class(base_class(),access);
}
void Class::set_base_class(Class *base, int access)
{
using namespace Parser;
clear();
if (base) m_parent = base;
m_base_access = access;
// *fix 1.1.2 Field alignment moved here from ctor, in case of redefinition.
set_dword_align(Parser::debug.class_dword_align);
if (base) {
PPClass vmt;
m_VMT = NULL; // hm, really shd clear out if NOT null...
// *NB* use the actual size, not the VMT-adjusted size!
m_data = (char *)m_parent->size();
m_slot_id = base->last_slot();
vmt = base->get_VMT();
int vmt_size = sizeof(void *)*(m_slot_id+1);
if (vmt) //...copy our parent's VMT into the local buffer
memcpy(mVMT,vmt,vmt_size);
copy_convert_to_list();
// if we are derived from an imported class w/ a vtable,
// then we will have to generate a vtable for these objects.
// *fix 1.2.0 Use the inherited vtable size
// *fix 1.2.7 Such classes must have the _same_ kind of import scheme
// as their bases; this was not working if there was a subsequent DLL load.
if((base->imported() && vmt) || (base->vtable_size() > 0)) {
m_vtable_size = base->vtable_size();
m_import = Import::create_scheme(base->import_scheme()->scheme_type());
m_imported = Builtin::get_dll_handle() != NULL;
m_import->set_first_virtual_class(base->m_import->vmt_offset());
}
// *hack 0.9.8 A forward declaration of ctor; hack for factory functions.
// *OUT*this interferes w/ normal default ctor rules.
//declare_method(constructor_name(),t_void,NULL,t_void,IsConstructor);
//m_base_ctor = true;
} else {
m_slot_id = 0;
}
}
Function *Class::default_constructor() { return m_default_constructor; }
Function *Class::copy_constructor() { return m_copy_constructor; }
Function *Class::destructor() { return m_destructor; }
bool Class::has_constructors()
{
return m_has_constructors;
}
// defines our convention for internally naming the constructor/destructor
// cf. parser.y line 110...
string Class::constructor_name()
{ return "__C__"; }
string Class::destructor_name()
{ return "__D__"; }
bool Class::is_anonymous_union()
{
return m_is_union && name()[0] == '$';
}
void Class::set_anonymous_union()
{
switch_off_allocator_advance();
m_is_union = true;
}
PEntry
Class::add(const string& name)
{
PEntry pe = Table::add(name);
pe->set_access(get_access_mode());
m_entries.push_back(pe);
return pe;
}
// compare this to Table::list_entries(), which uses the underlying map.
// Class keeps an explicit list of entries so it can deliver entries
// in _order of declaration_. No doubt there is more than one way to
// do this
void Class::list_entries(EntryList &el, int flags)
{
if (flags & DO_PARENT && base_class() != NULL) base_class()->list_entries(el,flags);
EntryList::iterator eli;
for(eli = m_entries.begin(); eli != m_entries.end(); ++eli)
if (check_entry(*eli,flags)) el.push_back(*eli);
}
void
Class::add_constructor(Function *fn, bool no_implicit_type_conversion)
{
if (fn->can_match(0)) {
m_default_constructor = fn;
} else
if (fn->can_match(1)) {
Type t = entry()->type; t.make_const_reference();
Type at = *(fn->signature()->begin()); // the type of the argument
// was this a copy constructor?
if (at == t) m_copy_constructor = fn;
else if (! no_implicit_type_conversion) {
// defines a Conversion From - _except_ for copy constructor and if marked 'explicit'!
m_from_list.push_back(at);
m_from_fn.push_back(fn);
}
}
m_has_constructors = true;
m_simple_struct = false;
}
void Class::destructor(Function *fn)
{
m_destructor = fn; // *change 1.0.0 does NOT imply this class has to have VMT
// *fix 1.2.0 This caused enormous grief - I'm not at all sure _why_ it was here!
// construct_VMT() shd _only_ be called when all the class' virtual slots are filled,
// i.e. at class finalization.
//construct_VMT(); // *temporary - depends on _gForceVMT
}
void Class::construct_VMT()
{
if (m_VMT) return; // already built one!
if (m_slot_id > 0) {
// increase our size to accomodate VMT
// - but only if this is the first class to have a VMT!
//if (!is_derived() || is_derived() && PClass(m_parent)->last_slot() == 0) m_data += 4;
mVMT[0] = PFBlock(this); // our class pointer!
m_VMT = new PClass [m_slot_id+1];
memcpy(m_VMT, mVMT, sizeof(PFBlock)*(m_slot_id+1));
}
}
void Class::clear()
{
Table::clear(); // just to test - not sure about inheritance!
m_has_constructors = false; // NB to reset state fully!
m_default_constructor = m_copy_constructor = m_destructor = NULL;
s_entry_map.clear();
m_obj_list.clear();
m_field_list.clear();
m_entries.clear();
m_vtable_slots.clear();
m_vtable_funs.clear();
m_base_entry = NULL;
m_VMT = NULL;
m_slot_id = 0;
}
Namespace *Class::inside_namespace()
{
if (Parser::is_namespace(entry()->context)) return (Namespace *)entry()->context;
else return NULL;
}
bool not_locally_defined(Class *pc, PEntry pe)
{
return !pe || !pc->is_local_entry(pe);
}
Type ref_to(Class *pc, bool is_const=true)
{
Type t_oref = pc->entry()->type; //??
t_oref.make_reference();
if (is_const) t_oref.make_const();
return t_oref;
}
PEntry Class::get_constructor()
{
return lookup(constructor_name());
}
static int s_first_line = -1, s_last_line;
void Class::add_line_no(const string& file, int line)
{
if (s_first_line == -1) s_first_line = line;
else s_last_line = line;
}
// *add 1.2.7 support for the defered compilation of method bodies
void Class::add_defered_method_body(Function* fn, char* body_buff)
{
m_DeferedBodies.push_back(new MethodBody(fn,body_buff));
}
void save_parser_state();
void restore_parser_state();
string s;
Class* mClass;
void* _copy_body(char* buff)
{
strcpy(buff,s.c_str());
return mClass;
}
void Class::compile_methods()
{
if (Parser::debug.skip_method_bodies) {
using namespace Parser;
Module *pm = Module::current();
//string s;
bool success;
int line;
mClass = this;
string file = pm->name();
debug.skip_method_bodies = false;
ParserState old_state = state;
save_parser_state(); // save the BISON state...
while (m_DeferedBodies.size() > 0) {
MethodBody* mb = m_DeferedBodies.front();
s = mb->body;
line = pm->get_function_line(mb->pf);
try {
success = uc_eval(s.c_str(),false,true,file.c_str(),line) == 0;
} catch(string msg) {
success = false;
}
if (! success) {
cerr << s << "*\n";
debug.skip_method_bodies = true;
m_DeferedBodies.clear();
return;
}
delete mb;
m_DeferedBodies.pop_front();
}
m_DeferedBodies.clear();
Input::insert_string(";");
state = old_state;
debug.skip_method_bodies = true;
restore_parser_state();
Parser::state.in_declaration = false; //*HACK*
}
}
void Class::finalize()
{
Module::current()->add_class(this,s_first_line,s_last_line);
s_last_line = -1;
construct_VMT();
// not entirely the whole story.....
m_simple_struct = !has_constructors();
m_finalized = true;
if (imported()) {
// *fix 1.2.0 Ensure that imported classes have a proper vtable
m_vtable_size = m_slot_id;
// *add 1.2.3 __vftable is used to reserve the class layout
// of an imported class with virtual methods, but only if it's the first such class.
if (! base_class() && m_vtable_size > 0) {
PEntry pe;
// in case of the vtable being at the begining, we have to shuffle entries along
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -