Commit f4308072 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

OOP: implement @property - part 1 (issue #611)


Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent e569fc38
......@@ -575,6 +575,11 @@ namespace Common {
* (i.e. it is defined in the body of a function, testcase, altstep or
* control part). */
virtual bool is_local() const;
/** Returns whether the @property modifier is set for the assignment.
* Only relevant for variable and template variable definitions.
*/
virtual bool is_property() const { return false; }
/** @name Need to be overridden and implemented in derived classes.
* Calling these methods causes a FATAL_ERROR.
*
......
......@@ -615,6 +615,38 @@ namespace Common {
return NULL;
}
bool Scope::is_in_getter_scope()
{
if (parent_scope != NULL) {
return parent_scope->is_in_getter_scope();
}
return NULL;
}
Ttcn::Getter* Scope::get_scope_getter()
{
if (parent_scope != NULL) {
return parent_scope->get_scope_getter();
}
return NULL;
}
bool Scope::is_in_setter_scope()
{
if (parent_scope != NULL) {
return parent_scope->is_in_setter_scope();
}
return NULL;
}
Ttcn::Setter* Scope::get_scope_setter()
{
if (parent_scope != NULL) {
return parent_scope->get_scope_setter();
}
return NULL;
}
bool Scope::has_ass_withId(const Identifier& p_id)
{
if (parent_scope) return parent_scope->has_ass_withId(p_id);
......
......@@ -63,6 +63,8 @@ namespace Ttcn {
class transparency_holder;
class Statement;
class ClassTypeBody;
class Getter;
class Setter;
} // namespace Ttcn
namespace Common {
......@@ -618,6 +620,10 @@ public:
virtual Module* get_scope_mod_gen();
virtual bool is_class_scope() const { return false; }
virtual Ttcn::ClassTypeBody* get_scope_class();
virtual bool is_in_setter_scope();
virtual Ttcn::Getter* get_scope_getter();
virtual bool is_in_getter_scope();
virtual Ttcn::Setter* get_scope_setter();
/** Returns the assignment referenced by \a p_ref. If no such
* node, 0 is returned. */
virtual Assignment* get_ass_bySRef(Ref_simple *p_ref) =0;
......
......@@ -456,29 +456,48 @@ namespace Ttcn {
// Template::generate_code_init_se, TypeConv::gen_conv_func_choice_anytype,
// defUnionClass and defUnionTemplate.
const Identifier& id = *ref->get_id();
// todo: convert back to non-const if the previous type wasn't a class and the current one is
expr->expr = mputprintf(expr->expr, "%s%s%s%s",
(type != NULL && type->get_typetype() == Type::T_CLASS) ? "->" : ".",
((type!=0 && type->get_typetype()==Type::T_ANYTYPE) ? "AT_" : ""),
id.get_name().c_str(),
(type != NULL && type->get_typetype() == Type::T_CLASS) ? "" : "()");
if (type) {
if (type->get_type_refd_last()->get_typetype() != Type::T_CLASS) {
CompField *cf = type->get_comp_byName(id);
// If the field is optional, the return type of the accessor is an
// OPTIONAL<T>. Write a call to OPTIONAL<T>::operator(),
// which "reaches into" the OPTIONAL to get the contained type T.
// Don't do this at the end of the reference chain.
// Accessor methods for a foo_template return a bar_template
// and OPTIONAL<> is not involved, hence no "()".
if (!is_template && i < n_refs - 1 && cf->get_is_optional())
expr->expr = mputstr(expr->expr, "()");
// Follow the field type.
type = cf->get_type();
}
else { // class
type = type->get_class_type_body()->
get_local_ass_byId(*ref->get_id())->get_Type();
bool is_class = type != NULL && type->get_typetype() == Type::T_CLASS;
Common::Assignment* ass = is_class ? type->get_class_type_body()->
get_local_ass_byId(*ref->get_id()) : NULL;
bool is_property = ass != NULL && ass->is_property();
if (is_property && !const_ref) {
// a property setter is a bit tricky, since it needs to look like
// 'x.set_y(z)' instead of 'x.y = z'
string tmp_id = ref_scope->get_scope_mod_gen()->get_temporary_id();
expr->preamble = mputprintf(expr->preamble, "%s %s;\n",
is_template ? ass->get_Type()->get_genname_template(ref_scope).c_str() :
ass->get_Type()->get_genname_value(ref_scope).c_str(),
tmp_id.c_str());
expr->postamble = mputprintf(expr->postamble, "%s->set_%s(%s);\n",
expr->expr, id.get_name().c_str(), tmp_id.c_str());
Free(expr->expr);
expr->expr = mcopystr(tmp_id.c_str());
}
else {
// todo: convert back to non-const if the previous type wasn't a class and the current one is
expr->expr = mputprintf(expr->expr, "%s%s%s%s%s",
is_class ? "->" : ".",
is_property ? "get_" : "",
((type!=0 && type->get_typetype()==Type::T_ANYTYPE) ? "AT_" : ""),
id.get_name().c_str(),
(is_class && !is_property) ? "" : "()");
if (type) {
if (!is_class) {
CompField *cf = type->get_comp_byName(id);
// If the field is optional, the return type of the accessor is an
// OPTIONAL<T>. Write a call to OPTIONAL<T>::operator(),
// which "reaches into" the OPTIONAL to get the contained type T.
// Don't do this at the end of the reference chain.
// Accessor methods for a foo_template return a bar_template
// and OPTIONAL<> is not involved, hence no "()".
if (!is_template && i < n_refs - 1 && cf->get_is_optional())
expr->expr = mputstr(expr->expr, "()");
// Follow the field type.
type = cf->get_type();
}
else { // class
type = ass->get_Type();
}
}
}
} else if (ref->get_type() == FieldOrArrayRef::FUNCTION_REF) {
......@@ -858,9 +877,12 @@ namespace Ttcn {
if (sb != NULL && sb->is_in_dynamic_template()) {
ass = sb->get_dynamic_template()->get_dynamic_formalpar();
}
else if (my_scope != NULL && my_scope->is_in_setter_scope()) {
ass = my_scope->get_scope_setter()->get_formalpar();
}
else {
error("Reference to value being matched is only allowed inside a "
"dynamic template's statement block");
error("Reference to `value' is only allowed inside a "
"dynamic template or property setter");
}
}
else {
......@@ -1310,6 +1332,23 @@ namespace Ttcn {
{
Common::Assignment *ass = get_refd_assignment();
if (!ass) FATAL_ERROR("Reference::generate_code()");
if (ass->is_property() && subrefs.get_nof_refs() == 0) {
// a property setter is a bit tricky, since it needs to look like
// 'set_x(y)' instead of 'x = y'
// (this case is handled by the subrefs code generator if there are subrefs)
string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
expr->preamble = mputprintf(expr->preamble, "%s %s;\n",
ass->get_asstype() == Definition::A_VAR ?
ass->get_Type()->get_genname_value(my_scope).c_str() :
ass->get_Type()->get_genname_template(my_scope).c_str(),
tmp_id.c_str());
expr->postamble = mputprintf(expr->postamble, "%sset_%s(%s);\n",
expr->expr != NULL ? expr->expr : "",
ass->get_genname_from_scope(my_scope).c_str(), tmp_id.c_str());
Free(expr->expr);
expr->expr = mcopystr(tmp_id.c_str());
return;
}
generate_class_specific_expr(expr);
if (reftype == REF_THIS && id == NULL) {
// 'this' with no field reference has already been handled by 'generate_class_specific_expr'
......@@ -1351,14 +1390,20 @@ namespace Ttcn {
void Reference::generate_code_const_ref(expression_struct_t *expr)
{
Common::Assignment* ass = get_refd_assignment();
if (!ass) FATAL_ERROR("Reference::generate_code_const_ref()");
FieldOrArrayRefs *t_subrefs = get_subrefs();
if (!t_subrefs || t_subrefs->get_nof_refs() == 0) {
generate_code(expr);
if (ass->is_property()) {
expr->expr = mputprintf(expr->expr, "get_%s()",
ass->get_genname_from_scope(my_scope).c_str());
}
else {
generate_code(expr);
}
return;
}
Common::Assignment *ass = get_refd_assignment();
if (!ass) FATAL_ERROR("Reference::generate_code_const_ref()");
bool is_template;
switch (ass->get_asstype()) {
......@@ -2588,11 +2633,10 @@ namespace Ttcn {
{
bool in_class = parent_scope->is_class_scope();
for (size_t i = 0; i < ass_v.size(); i++) {
if (in_class && ass_v[i]->get_asstype() != Common::Assignment::A_CONSTRUCTOR) {
visibility_t vis = ass_v[i]->get_visibility();
if (in_class && !ass_v[i]->is_property() &&
ass_v[i]->get_asstype() != Common::Assignment::A_CONSTRUCTOR) {
target->header.class_defs = mputprintf(target->header.class_defs,
"\n%s:\n", vis == PUBLIC ? "public" : (vis == PRIVATE ? "private" :
"protected"));
"\n%s:\n", ClassTypeBody::get_visibility_str(ass_v[i]->get_visibility()));
}
ass_v[i]->generate_code(target);
}
......@@ -5881,6 +5925,83 @@ namespace Ttcn {
type->get_genname_value(my_scope).c_str(), id->get_name().c_str(), type->get_genname_value(my_scope).c_str());
}
// =================================
// ===== Def_Property
// =================================
Def_Property::Def_Property(Identifier* p_id, Type* p_type, Value* p_initial_value,
PropertyBody* p_body)
: Def_Var(p_id, p_type, p_initial_value), body(p_body)
{
}
Def_Property::~Def_Property()
{
delete body;
}
void Def_Property::set_fullname(const string& p_fullname)
{
Def_Var::set_fullname(p_fullname);
if (body != NULL) {
body->set_fullname(p_fullname);
}
}
void Def_Property::chk()
{
if (checked) {
return;
}
Def_Var::chk();
if (body != NULL) {
body->chk(this);
}
}
void Def_Property::use_as_lvalue(const Location& p_loc)
{
if (body != NULL && body->get_setter() == NULL) {
p_loc.error("Cannot modify property `%s' because it doesn't have a setter",
id->get_dispname().c_str());
}
}
void Def_Property::generate_code(output_struct* target, bool clean_up)
{
type->generate_code(target);
if (body != NULL) {
body->generate_code(target);
}
else {
// the automatic property generates a member variable with the property name,
// that stores the current value of a property;
// the automatic getter and setter gets and sets this member (just like with record fields);
// this member is always private, while the getter and setter use the property's visibility
target->header.class_defs = mputprintf(target->header.class_defs,
"\nprivate:\n");
const string& name = get_genname();
const_def cdef;
Code::init_cdef(&cdef);
type->generate_code_object(&cdef, my_scope, name, NULL, false, false, true);
Code::merge_cdef(target, &cdef, true);
Code::free_cdef(&cdef);
target->header.class_defs = mputprintf(target->header.class_defs,
"\n%s:\n", ClassTypeBody::get_visibility_str(visibilitytype));
const string& type_str = type->get_genname_value(my_scope);
const string& class_name = my_scope->get_scope_class()->get_id()->get_name();
Getter::generate_code_automatic(target, type_str, name, class_name);
Setter::generate_code_automatic(target, type_str, name, class_name);
}
if (initial_value != NULL) {
char*& init = target->temp.constructor_preamble;
string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
init = mputprintf(init, "%s %s;\n", type->get_genname_value(my_scope).c_str(), tmp_id.c_str());
init = initial_value->generate_code_init(init, tmp_id.c_str());
init = mputprintf(init, "set_%s(%s);\n", get_genname().c_str(), tmp_id.c_str());
}
}
// =================================
// ===== Def_Var_Template
// =================================
......@@ -6156,6 +6277,22 @@ namespace Ttcn {
if (initial_value) initial_value->dump(level + 1);
}
// =================================
// ===== Def_Property_Template
// ================================
Def_Property_Template::Def_Property_Template(Identifier* p_id, Type* p_type,
Template* p_initial_value, template_restriction_t p_template_restriction,
PropertyBody* p_body)
: Def_Var_Template(p_id, p_type, p_initial_value, p_template_restriction), body(p_body)
{
}
Def_Property_Template::~Def_Property_Template()
{
delete body;
}
// =================================
// ===== Def_Timer
// =================================
......@@ -8753,8 +8890,6 @@ namespace Ttcn {
Free(formal_par_list);
}
// TODO
// =================================
// ===== Def_Altstep
......
......@@ -38,8 +38,6 @@ namespace Ttcn {
* @{
*/
using namespace Common;
class Module;
class Definition;
class FriendMod;
......@@ -63,6 +61,10 @@ namespace Ttcn {
class ErroneousAttributes;
class ErroneousAttributeSpec;
class PrintingType;
class PropertyBody;
class Assignment; // Ttcn::Assignment is not the same as Common::Assignment
using namespace Common;
/** Class to represent an actual parameter */
class ActualPar : public Node {
......@@ -1197,14 +1199,15 @@ namespace Ttcn {
protected:
Type *type;
private:
/** the initial value: optional and maybe incomplete */
Value *initial_value;
private:
/// Copy constructor disabled
Def_Var(const Def_Var& p);
/// %Assignment disabled
Def_Var& operator=(const Def_Var& p);
public:
Def_Var(Identifier *p_id, Type *p_type, Value *p_initial_value);
virtual ~Def_Var();
......@@ -1240,6 +1243,27 @@ namespace Ttcn {
virtual char* generate_code_str(char *str);
};
class Def_Property : public Def_Var {
PropertyBody* body;
/// Copy constructor disabled
Def_Property(const Def_Property& p);
/// %Assignment disabled
Def_Property& operator=(const Def_Property& p);
public:
Def_Property(Identifier* p_id, Type* p_type, Value* p_initial_value,
PropertyBody* p_body);
virtual ~Def_Property();
virtual bool is_property() const { return true; }
bool is_automatic() const { return body == NULL; }
virtual void set_fullname(const string& p_fullname);
virtual void chk();
virtual void use_as_lvalue(const Location& p_loc);
// todo: use as rvalue
virtual void generate_code(output_struct* target, bool clean_up = false);
};
/**
* Def_Var_Template class represents a template variable (dynamic template)
* definition.
......@@ -1281,6 +1305,22 @@ namespace Ttcn {
{ return template_restriction; }
};
class Def_Property_Template : public Def_Var_Template {
PropertyBody* body;
/// Copy constructor disabled
Def_Property_Template(const Def_Property_Template& p);
/// %Assignment disabled
Def_Property_Template& operator=(const Def_Property_Template& p);
public:
Def_Property_Template(Identifier* p_id, Type* p_type, Template* p_initial_value,
template_restriction_t p_template_restriction, PropertyBody* p_body);
virtual ~Def_Property_Template();
virtual bool is_property() const { return true; }
bool is_automatic() const { return body == NULL; }
};
/**
* Def_Timer class represents a single timer declaration (e.g. in a
* TTCN component type).
......
......@@ -460,7 +460,7 @@ namespace Ttcn {
/* actually, not supported */
break;
case ps_elem_t::PSE_REF: {
Ttcn::Assignment *ass = pse->ref->get_refd_assignment();
Common::Assignment *ass = pse->ref->get_refd_assignment();
if (ass == lhs) return true;
break; }
} // switch
......
......@@ -4043,6 +4043,39 @@ error:
return;
}
}
else if (my_sb->is_in_getter_scope()) {
Definition* property = my_sb->get_scope_getter()->get_my_def();
bool is_template = property->get_asstype() == Definition::A_VAR_TEMPLATE;
if (returnexpr.t == NULL) {
error("Missing return value");
goto error;
}
else if (!is_template && !returnexpr.t->is_Value()) {
returnexpr.t->error("A specific value without matching symbols was "
"expected as return value");
goto error;
}
else {
Type* return_type = property->get_Type();
if (is_template) {
returnexpr.t->set_my_governor(return_type);
returnexpr.t->flatten(false);
return_type->chk_this_template_ref(returnexpr.t);
return_type->chk_this_template_generic(returnexpr.t, INCOMPLETE_NOT_ALLOWED,
OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK, IMPLICIT_OMIT, NOT_CLASS_MEMBER_INIT, NULL);
}
else {
returnexpr.v = returnexpr.t->get_Value();
delete returnexpr.t;
returnexpr.t = 0;
returnexpr.v->set_my_governor(return_type);
return_type->chk_this_value_ref(returnexpr.v);
return_type->chk_this_value(returnexpr.v, 0, Type::EXPECTED_DYNAMIC_VALUE,
INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
}
return;
}
}
else { // control part
error("Return statement cannot be used in the control part. "
"It is allowed only in functions and altsteps");
......@@ -6475,6 +6508,14 @@ error:
error("The @update statement is not allowed in the statement block of a dynamic template");
return;
}
if (my_sb->is_in_getter_scope()) {
error("The @update statement is not allowed in the statement block of a property's getter");
return;
}
if (my_sb->is_in_setter_scope()) {
error("The @update statement is not allowed in the statement block of a property's setter");
return;
}
Common::Assignment* refd_ass = update_op.ref->get_refd_assignment(false);
if (refd_ass != NULL) {
switch (refd_ass->get_asstype()) {
......@@ -9869,6 +9910,7 @@ error:
{
FieldOrArrayRefs *t_subrefs = ref->get_subrefs();
const bool rhs_copied = self_ref;
Common::Assignment* refd_ass = ref->get_refd_assignment();
switch (asstype) {
case ASS_VAR: {
const string& rhs_copy = val->get_temporary_id();
......@@ -9934,7 +9976,7 @@ error:
str = mputstr(str, "}\n");
}
else {
if (t_subrefs) {
if (t_subrefs || refd_ass->is_property()) {
if (!val->has_single_expr()) goto case3;
// C++ equivalent of RHS is a single expression.
expression_struct expr;
......@@ -9957,7 +9999,6 @@ error:
}
else {
// The LHS is a single identifier.
Common::Assignment* refd_ass = ref->get_refd_assignment();
string rhs_name = refd_ass->get_genname_from_scope(ref->get_my_scope());
if (in_constructor &&
refd_ass->get_asstype() == Common::Assignment::A_CONST) {
......@@ -10046,7 +10087,7 @@ error:
str = mputstr(str, "}\n");
}
else { // !needs_conv
if (t_subrefs) {
if (t_subrefs || refd_ass->is_property()) {
if ((template_restriction == TR_NONE || !gen_restriction_check)
&& templ->has_single_expr()) {
// C++ equivalent of RHS is a single expression and no restriction
......@@ -10073,7 +10114,6 @@ error:
}
else {
// LHS is a single identifier
Common::Assignment* refd_ass = ref->get_refd_assignment();
string rhs_name = refd_ass->get_genname_from_scope(ref->get_my_scope());
if (in_constructor &&
refd_ass->get_asstype() == Common::Assignment::A_TEMPLATE) {
......@@ -10093,9 +10133,8 @@ error:
}
if (template_restriction != TR_NONE && gen_restriction_check)
str = Template::generate_restriction_check_code(str,
ref->get_refd_assignment()->get_genname_from_scope(
ref->get_my_scope()
).c_str(), template_restriction);
refd_ass->get_genname_from_scope(ref->get_my_scope()).c_str(),
template_restriction);
}
}
if (rhs_copied) {
......
......@@ -3781,10 +3781,14 @@ namespace Ttcn {
for (size_t i = 0; i < members->get_nof_asss(); ++i) {
Common::Assignment* ass = members->get_ass_byIndex(i, false);
switch (ass->get_asstype()) {
case Common::Assignment::A_CONST:
case Common::Assignment::A_VAR:
case Common::Assignment::A_TEMPLATE:
case Common::Assignment::A_VAR_TEMPLATE:
if (ass->is_property()) {
break;
}
// otherwise fall through
case Common::Assignment::A_CONST:
case Common::Assignment::A_TEMPLATE:
if (ass->get_visibility() == PUBLIC) {
ass->error("Class members cannot be public");
}
......@@ -3792,10 +3796,10 @@ namespace Ttcn {
case Common::Assignment::A_EXT_FUNCTION:
case Common::Assignment::A_EXT_FUNCTION_RVAL:
case Common::Assignment::A_EXT_FUNCTION_RTEMP:
if (!external) {
if (!external) {
ass->error("Internal classes cannot have external methods or methods with no body");
}
break;
}
break;
default:
break;
}
......@@ -3943,62 +3947,80 @@ namespace Ttcn {
}
StatementBlock* block = new StatementBlock();
for (size_t i = 0; i < members->get_nof_asss(); ++i) {
// note: the Definitions class rearranges its elements alphabetically;
// here the members must be accessed in their original order
Common::Assignment* member = members->get_ass_byIndex(i, false);
bool is_template = false;
TemplateInstance* def_val = NULL;
switch (member->get_asstype()) {
case Common::Assignment::A_CONST:
if (member->get_Value() != NULL) {
continue; // the constant has already been initialized at its definition
}
break;
case Common::Assignment::A_TEMPLATE:
if (member->get_Template() != NULL) {
continue; // the template has already been initialized at its definition
}
is_template = true;
break;
case Common::Assignment::A_VAR:
if (member->get_Value() != NULL) {
// set the variable's initial value as the constructor parameter's default value
Def_Var* var_member = dynamic_cast<Def_Var*>(member);
if (var_member == NULL) {
FATAL_ERROR("ClassTypeBody::chk - Def_Var cast");
}
def_val = new TemplateInstance(NULL, NULL, new Template(var_member->steal_Value()));
}
break;
case Common::Assignment::A_VAR_TEMPLATE:
is_template = true;
if (member->get_Template() != NULL) {
// set the template variable's initial value as the constructor parameter's default value
Def_Var_Template* var_temp_member = dynamic_cast<Def_Var_Template*>(member);
if (var_temp_member == NULL) {
FATAL_ERROR("ClassTypeBody::chk - Def_Var_Template cast");
}
def_val = new TemplateInstance(NULL, NULL, var_temp_member->steal_Template());
}
break;