📄 common.cpp.svn-base
字号:
// *fix 1.2.4 When breaking out of a loop inside a local context, do check that
// there _actually is_ a context local to the loop! We were trashing objects!
// *fix 1.2.5 The new 'false' param for LocalContext::check_objects() prevents
// the system for considering this context to be fully unwound. We were losing
// normal unwind at the end of loops.
bool check_local_unwind()
{
if (is_local(&state.context()) && state.context_generated())
return ((LocalContext&)state.context()).check_objects(false);
else return false;
}
bool do_break_continue(int keyword)
{
LabelStack &lstack = (keyword==BREAK) ? state.break_stack : state.continue_stack;
if (lstack.depth()==0) { error("break/continue not allowed here"); return false; }
if (keyword==BREAK) check_local_unwind();
code().jump(JMP,lstack.TOS());
return true;
}
///// implementing switch statements
void do_switch(PExpr e)
{
state.in_switch = true;
push_label_stack(); // for the break....
code().compile(e);
state.switch_stack.push(new SwitchBlock(&code()));
code().emit(JSWITCH,DIRECT,0);
}
bool do_case(PExpr e)
{
if (e) {
void *cnst_ptr = ExprToPtr(e);
int val;
if (cnst_ptr==NULL) { error("Case label cannot be an expression"); return false; }
switch(e->type().size()) {
case sizeof(long): val = *(long *)cnst_ptr; break;
case sizeof(short): val = *(short*)cnst_ptr; break;
case sizeof(char): val = *(char *)cnst_ptr; break;
default: { error("Case label must be an integer type"); return false; }
}
state.switch_stack.TOS()->add_jump(val);
}else state.switch_stack.TOS()->add_default();
return true; // for now
}
void end_switch()
{
state.break_stack.TOS()->here();
delete state.switch_stack.pop()->construct();
}
////////// implementing goto
static Label* create_goto_label(char* label_name)
{
// NB to create the label in the overall function context, not some local block
Table* cntxt = ((LocalContext&)state.context()).function()->context();
PEntry label_entry = cntxt->add(label_name);
label_entry->type = t_label;
label_entry->size = 0;
Label* label = new Label(&code());
label_entry->data = (int)label;
return label;
}
static Label* check_goto_label(PEntry label_entry)
{
if (! state.return_label())
fail("goto only allowed in functions");
if (label_entry->type != t_label)
fail(label_entry->name + " is already defined");
return (Label*)label_entry->data;
}
void do_goto(char* label_name)
{
try {
Label* label = NULL;
Table* cntxt = &state.context();
PEntry label_entry = cntxt->lookup(label_name);
if (label_entry) label = check_goto_label(label_entry);
if (! label) { // ----not declared yet; forward jump----
label = create_goto_label(label_name);
// if there were local objects needing destruction, keep track of this!
if (check_local_unwind()) label->context(cntxt);
} else { // ----backward jump---------------------
// only try to unwind local objects if the actual label was outside our context
// (if the label doesn't have an address, then we haven't found it yet)
if (label->context() != cntxt || ! label->addr_is_set()) check_local_unwind();
}
// and do the jump!
code().jump(JMP,label);
} catch(string s) {
error(s);
}
}
void goto_label_new(char* name)
{
Label* l = create_goto_label(name);
l->here();
}
void goto_label_existing(PEntry pe)
{
try {
Label* l = check_goto_label(pe);
l->here();
if (l->context() == &state.context()) l->patch_with_NOP();
} catch(string s) {
error(s);
}
}
string name_of(Type t)
{
static string s;
t.as_string(s);
return s;
}
bool ParserState::begin_scope(Type t)
{
if (!t.is_class()) {
error(quotes(name_of(t)) + " is not a class, struct or namespace");
return false;
} else {
PClass pc = t.as_class();
//* this forces instantiation of templates when we need to access their members
if (! t.is_namespace()) pc->make_available();
push_context(pc);
scope_context_stack.push(scope_context);
scope_context = &context();
gScopeContext = scope_context;
return true;
}
}
// *add 1.2.7 backing scope_context with a stack...
void ParserState::end_scope()
{
pop_context();
scope_context = scope_context_stack.pop();
}
PClass class_context()
{
return is_class(&state.context()) ? PClass(&state.context()) : NULL;
}
void error_already_defined(const string& name)
{
if (! interactive_mode())
fail(quotes(name) + " is already defined");
}
// *add 1.2.6 union
Type ParserState::add_class(int s_or_c, const string& pname, int deriv_access, Type base_class)
{
bool is_struct = s_or_c != CLASS;
try {
Table *instantiation_context = NULL;
Table *cntxt = &context();
// *add 1.1.4
// * tagless structs are given a temporary name
// * in C mode, structs and unions are made distinct so they don't conflict w/ variable names ('struct tm tm')
string name = pname;
if (name == "") name = make_temp_name();
else if (debug.c_mode && is_struct) name = "$_" + name;
PEntry pe = context().lookup(name);
bool already_defined = pe != NULL;
//*SJD* ISSUE Policy is a little loose here; I'm allowing redefinition
// but really it is another concession to interactive mode.
// if(already_defined && context().is_local_entry(pe)) error_already_defined(name);
if(!already_defined) {
// *fix 0.9.6 a template class is w/in an 'instantiation context' - watch out for nested case!
// *fix 0.9.8 the IC is _injected_ into the class context - parent trick breaks down w/ derivation
// if (in_template == NULL || context().type() != INSTANTIATION_CONTEXT)
// pe = context().add(name);
// else pe = context().parent_context()->add(name); // the enclosing namespace
if (in_template != NULL && context().type() == INSTANTIATION_CONTEXT) {
instantiation_context = pop_context();
cntxt = instantiation_context->parent_context();
}
pe = cntxt->add(name);
//if (deriv_access == ForwardClass && in_typedef)
}
Class *base = NULL;
if (deriv_access != NotDerived && deriv_access != ForwardClass) {
string nb = quotes(as_str(base_class));
if (!base_class.is_class()) fail(nb + " is not a class");
base = base_class.as_class();
if (! base->make_available()) fail(nb + " is not fully defined");
}
if (deriv_access == Default) deriv_access = is_struct ? Public : Private;
Class *new_class;
if (! already_defined) {
new_class = new Class(base ? base : cntxt /*&context()*/,deriv_access);
new_class->entry(pe);
// *add 1.2.3 Class now keeps track of whether it's declared as struct or not
if (is_struct) new_class->make_struct();
new_class->set_access_mode(is_struct ? Public : Private);
if (s_or_c == UNION) new_class->set_anonymous_union();
pe->type = Type(new_class);
pe->size = 0;
pe->data = 0; // which distinguishes us from Namespaces!!
pe->set_class(); // meaning: we are not a typedef!
pe->m_typename = true;
if (instantiation_context != NULL) {
new_class->inject_namespace((Namespace *)instantiation_context);
push_context(instantiation_context);
}
} else if (deriv_access != ForwardClass) {
// *fix 1.1.2 A forward class definition must not clear out an existing class
// redefinition or full definition.
new_class = pe->type.as_class();
// two cases: full defn of forward class,
// and redefinition of existing class (an extension!)
//*SJD* Absolutely need to call the base-setting code for derived classes
new_class->set_base_class(base,deriv_access);
}
if (deriv_access != ForwardClass) {
if (state.in_template != NULL)
state.in_template->instantiated(true);
Module::current()->add_typename(pe);
push_context(new_class);
state.in_class = true;
state.class_dcl = t_void;
}
return pe->type;
} catch(string msg) { error(msg); }
return t_void;
}
void ParserState::set_access_mode(int val)
{
if (!is_class(&context())) error("Access mode not allowed here");
else ((Class&)context()).set_access_mode(val);
}
void ParserState::add_friend_class(const string& name)
{
if (!is_class(&context())) error("Friend not allowed here");
else {
PEntry pe = symbol_lookup(name);
if (!pe->type.is_class()) error("Must be a class context");
else ((Class&)context()).add_friend_object(pe->type.as_class(),true);
}
}
/*REF:NAMESPACE*/
void ParserState::add_namespace(string name)
{
Namespace *ns;
// *fix 1.2.0 Change in grammar for 'class_name' broke anonymous namespaces.
// *fix 1.2.3 Nasty code that was too free with string char data caused anon namespaces
// to break in debug mode.
bool is_anonymous = name == "";
if (is_anonymous) name = Module::anonymous_namespace_name();
PEntry pe = context().lookup(name);
bool already_defined = pe != NULL;
if(!already_defined) {
pe = context().add(name);
ns = new Namespace(&context());
pe->data = (int)ns;
pe->set_class();
ns->entry(pe);
pe->type = Type(ns);
pe->m_typename = true;
} else ns = (Namespace *)pe->data;
if(is_anonymous) context().inject_namespace(ns);
else Module::add_namespace(ns);
push_context(ns);
}
Signature *ParserState::get_signature(Type t, bool clear_arglist)
{
// 'clear_arglist' now (apart from doing what it says afterwards)
// also attaches the list of parameter names to the signature.
TypeList ts;
int sz = 0;
bool was_stdarg = false;
ArgList::iterator ali;
StringList sl;
if (arg_list.back().type == t_void) { // means '...'!!
arg_list.pop_back();
was_stdarg = true;
}
for(ali = arg_list.begin(); ali != arg_list.end(); ++ali) {
ts.push_back(ali->type);
// *fix 1.2.2 Displayed prototypes of imported functions had extra '*' symbols;
// they were probably put in to signify 'no name'
if (ali->name != "*") sl.push_back(ali->name);
else sl.push_back("");
sz += (ts.back().is_double() ? sizeof(double) : sizeof(int));
}
Table *pc = &context();
// *fix 1.1.3 Member function template being instantiated!
if (pc->type() == INSTANTIATION_CONTEXT) pc = pc->parent_context();
Class *cptr = is_class(pc) ? (Class *)pc : NULL;
//HACK15 If this is a method pointer declaration, then use
// state.class_dcl which is set in the declaration grammar
if (was_fun_ptr && state.class_dcl != t_void) {
cptr = state.class_dcl.as_class();
state.class_dcl = t_void;
}
Signature *sig = new Signature(t,ts,cptr);
sig->byte_size(sz);
sig->stdarg(was_stdarg);
if (member_is_const) sig->set_const();
member_is_const = false;
// *fix 1.1.2 Argument names are now always saved by get_signature()
sig->set_arg_names(sl);
if (clear_arglist) end_args();
//*HACK* This can be set by array args...
// (usually reset by add_variable)
s_array_size = 1;
s_twodim_array = 1;
return sig;
}
// *fix 1.1.3 This is only called by do_function_template(), but
// there are two cases (i) a method of a template class declared outside,
// and (ii) a genuine method template.
Signature *get_prototype(bool& outside_class)
{
string name = state.token_stack.pop();
Type rtype = Parser::tpop(); //??
dcl_reset();
Signature::set_fun_name(name);
Signature *sig = state.get_signature(rtype,true);
outside_class = false;
if (state.class_dcl != t_void) {
sig->set_class_ptr(state.class_dcl.as_class());
state.class_dcl = t_void;
outside_class = true;
}
return sig;
}
Type ParserState::signature_type(Type rt)
{
// *fix 1.1.0 Building up function ptr type (they are declared like any other var...)
// function pointer declaration!!
was_fun_ptr = true;
Signature *sig = get_signature(rt,true);
// *fix 0.9.4 NB that signature ptrs are unique in fun ptr declarations.
rt = Type(unique_signature(sig));
rt.incr_pointer();
was_fun_ptr = false;
return rt;
}
PExprList get_default_arguments()
{
PExprList pel = new ExprList;
bool gotcha = false;
ArgList::iterator ali;
for(ali = state.arg_list.begin(); ali != state.arg_list.end(); ++ali)
if (ali->init) { pel->push_back(ali->init); gotcha = true; }
else if (gotcha) error("Default arguments must be at the end");
else pel->push_back(NULL);
if (!gotcha) {
delete pel; return NULL;
} else return pel;
}
static bool mIsConversion = false;
FunctionEntry *
create_function_entry(Signature *sig, const string& name, PEntry pe)
{
if (pe==NULL) pe = state.context().add(name);
mIsConversion = name == CONVERSION_OPNAME;
pe->type = Type(sig);
pe->name = name;
pe->rmode = DIRECT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -