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

Implemented object-oriented features - stage 6.1 (bug 552011)



Change-Id: Ief2350ee8c8a5d979e8c1efc7e564d691a91fba7
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent e2b8383c
......@@ -39,6 +39,7 @@
#include "ttcn3/profiler.h"
#include "ttcn3/Attributes.hh"
#include "ttcn3/Ttcnstuff.hh"
#include "ttcn3/Statement.hh"
namespace Common {
......@@ -676,6 +677,9 @@ namespace Common {
case Type::T_ALTSTEP:
typetype_name = "altstep";
break;
case Type::T_CLASS:
typetype_name = "class";
break;
default:
FATAL_ERROR("Scope::chk_runs_on_clause()");
typetype_name = 0;
......@@ -727,6 +731,33 @@ namespace Common {
}
}
void Scope::chk_mtc_clause(Type* p_type, const Location& p_loc)
{
// component type of the referred definition
Type* refd_comptype = p_type->get_class_type_body()->get_MtcType();
// definitions without 'mtc' can be called from anywhere
if (refd_comptype == NULL) {
return;
}
if (get_statementblock_scope()->get_my_def() == NULL) { // in control part
p_loc.error("Cannot create value of class type `%s', which has an `mtc' "
"clause, in the control part.", p_type->get_typename().c_str());
return;
}
Type* t_comptype = get_mtc_system_comptype(false);
if (t_comptype != NULL) {
if (!refd_comptype->is_compatible_component_by_port(t_comptype)) {
// the 'mtc' clause of the referred definition is not compatible
// with that of the current scope (i.e. the referring definition)
p_loc.error("Mtc clause mismatch: A definition that runs on component "
"type `%s' cannot create a value of class type `%s', which has `mtc' "
"component type `%s'",
t_comptype->get_typename().c_str(), p_type->get_typename().c_str(),
refd_comptype->get_typename().c_str());
}
}
}
void Scope::chk_system_clause(Assignment *p_ass, const Location& p_loc,
const char *p_what, bool in_control_part)
{
......@@ -751,6 +782,33 @@ namespace Common {
}
}
}
void Scope::chk_system_clause(Type* p_type, const Location& p_loc)
{
// component type of the referred definition
Type* refd_comptype = p_type->get_class_type_body()->get_SystemType();
// definitions without 'system' can be called from anywhere
if (refd_comptype == NULL) {
return;
}
if (get_statementblock_scope()->get_my_def() == NULL) { // in control part
p_loc.error("Cannot create value of class type `%s', which has a `system' "
"clause, in the control part.", p_type->get_typename().c_str());
return;
}
Type* t_comptype = get_mtc_system_comptype(true);
if (t_comptype != NULL) {
if (!refd_comptype->is_compatible_component_by_port(t_comptype)) {
// the 'system' clause of the referred definition is not compatible
// with that of the current scope (i.e. the referring definition)
p_loc.error("System clause mismatch: A definition that runs on component "
"type `%s' cannot create a value of class type `%s', which has `system' "
"component type `%s'",
t_comptype->get_typename().c_str(), p_type->get_typename().c_str(),
refd_comptype->get_typename().c_str());
}
}
}
// =================================
// ===== Reference
......
......@@ -634,10 +634,11 @@ public:
* "activate". */
void chk_runs_on_clause(Assignment *p_ass, const Location& p_loc,
const char *p_what);
/** Checks the 'runs on' clause of type \a p_fat that the values of it can
* be called from this scope unit. Type \a p_fat shall be of type function
* or altstep. Parameters \a p_loc and \a p_what are used in error messages.
* \a p_what contains "call" or "activate". */
/** Checks the 'runs on' clause of type \a p_fat that values of it can
* be called/created from this scope unit.
* Type \a p_fat shall be of function, altstep or class type.
* Parameters \a p_loc and \a p_what are used in error messages.
* \a p_what contains "call", "activate" or "create". */
void chk_runs_on_clause(Type *p_fat, const Location& p_loc,
const char *p_what);
/** Checks the 'mtc' clause of definition \a p_ass that it can
......@@ -646,12 +647,20 @@ public:
* "activate". */
void chk_mtc_clause(Assignment *p_ass, const Location& p_loc,
const char *p_what, bool in_control_part);
/** Checks the 'mtc' clause of type \a p_type that values of it can
* be created from this scope unit. Type \a p_type shall be of class type.
* Parameters \a p_loc is used in error messages. */
void chk_mtc_clause(Type* p_type, const Location& p_loc);
/** Checks the 'system' clause of definition \a p_ass that it can
* be called from this scope unit. Parameters \a p_loc and \a
* p_what are used in error messages. \a p_what contains "call" or
* "activate". */
void chk_system_clause(Assignment *p_ass, const Location& p_loc,
const char *p_what, bool in_control_part);
/** Checks the 'system' clause of type \a p_type that values of it can
* be created from this scope unit. Type \a p_type shall be of class type.
* Parameters \a p_loc is used in error messages. */
void chk_system_clause(Type* p_type, const Location& p_loc);
};
/**
......
......@@ -53,6 +53,7 @@
#include "ttcn3/Templatestuff.hh"
#include "ttcn3/RawAST.hh"
#include "ttcn3/JsonAST.hh"
#include "ttcn3/Statement.hh"
#include "../common/static_check.h"
#include "PredefFunc.hh"
......@@ -227,6 +228,7 @@ namespace Common {
case T_VERDICT:
case T_COMPONENT:
case T_DEFAULT:
case T_CLASS: // for the built-in class 'object'
break; // we have a pool type
default:
return 0; // no pool type for you!
......@@ -867,6 +869,9 @@ namespace Common {
case T_ADDRESS:
u.address = 0;
break;
case T_CLASS:
u.class_ = new Ttcn::ClassTypeBody();
break;
default:
FATAL_ERROR("Type::Type()");
} // switch
......@@ -1776,66 +1781,91 @@ namespace Common {
return 0;
}
Ttcn::ClassTypeBody* class_ = t->get_class_type_body();
bool base_toString = false;
if (!class_->has_local_ass_withId(id)) {
ref->error("Reference to non-existent method `%s' in class type `%s'",
id.get_dispname().c_str(), t->get_typename().c_str());
return 0;
}
Assignment* ass = class_->get_local_ass_byId(id);
if (!class_->chk_visibility(ass, ref, subrefs->get_my_scope())) {
// the method is not visible (the error has already been reported)
return 0;
}
switch (ass->get_asstype()) {
case Assignment::A_VAR:
case Assignment::A_VAR_TEMPLATE:
case Assignment::A_CONST:
case Assignment::A_TEMPLATE:
ref->error("Invalid reference to member `%s' in class type `%s', "
"reference to a method was expected instead",
id.get_dispname().c_str(), t->get_typename().c_str());
return 0;
case Assignment::A_FUNCTION:
case Assignment::A_EXT_FUNCTION:
if (i != nof_refs - 1 || last_method == NULL) {
ref->error("Invalid reference to method `%s' with no return type in "
"class type `%s'",
if (id.get_name() == string("toString")) {
// the 'toString' method is not in the AST, but it is inherited by
// every class from the 'object' class
base_toString = true;
if (!ref->parameters_checked()) {
Ttcn::FormalParList fp_list; // empty formal parameter list
Ttcn::ParsedActualParameters* parsed_pars = ref->get_parsed_pars();
Ttcn::ActualParList* ap_list = new Ttcn::ActualParList;
bool is_erroneous = fp_list.fold_named_and_chk(parsed_pars, ap_list);
if (is_erroneous) {
delete ap_list;
return 0;
}
ap_list->set_fullname(parsed_pars->get_fullname());
ap_list->set_my_scope(parsed_pars->get_my_scope());
ref->set_actual_par_list(ap_list);
}
t = get_pooltype(T_USTR);
// todo: set *last_method
}
else {
ref->error("Reference to non-existent method `%s' in class type `%s'",
id.get_dispname().c_str(), t->get_typename().c_str());
return 0;
}
// a method with no return value can still be valid (e.g. if it's
// in a statement)
// proceed as normal and return null at the end
case Assignment::A_FUNCTION_RVAL:
case Assignment::A_FUNCTION_RTEMP:
case Assignment::A_EXT_FUNCTION_RVAL:
case Assignment::A_EXT_FUNCTION_RTEMP: {
Ttcn::Def_Function_Base* def_func =
dynamic_cast<Ttcn::Def_Function_Base*>(ass);
if (def_func == NULL) {
FATAL_ERROR("Type::get_field_type");
}
if (!base_toString) {
Assignment* ass = class_->get_local_ass_byId(id);
if (!class_->chk_visibility(ass, ref, subrefs->get_my_scope())) {
// the method is not visible (the error has already been reported)
return 0;
}
t = def_func->get_return_type();
if (!ref->parameters_checked()) {
Ttcn::FormalParList* fp_list = ass->get_FormalParList();
Ttcn::ParsedActualParameters* parsed_pars = ref->get_parsed_pars();
Ttcn::ActualParList* ap_list = new Ttcn::ActualParList;
bool is_erroneous = fp_list->fold_named_and_chk(parsed_pars, ap_list);
if (is_erroneous) {
delete ap_list;
switch (ass->get_asstype()) {
case Assignment::A_VAR:
case Assignment::A_VAR_TEMPLATE:
case Assignment::A_CONST:
case Assignment::A_TEMPLATE:
ref->error("Invalid reference to member `%s' in class type `%s', "
"reference to a method was expected instead",
id.get_dispname().c_str(), t->get_typename().c_str());
return 0;
case Assignment::A_FUNCTION:
case Assignment::A_EXT_FUNCTION:
if (i != nof_refs - 1 || last_method == NULL) {
ref->error("Invalid reference to method `%s' with no return type in "
"class type `%s'",
id.get_dispname().c_str(), t->get_typename().c_str());
return 0;
}
ap_list->set_fullname(parsed_pars->get_fullname());
ap_list->set_my_scope(parsed_pars->get_my_scope());
ref->set_actual_par_list(ap_list);
}
if (last_method != NULL) {
*last_method = ass;
// a method with no return value can still be valid (e.g. if it's
// in a statement)
// proceed as normal and return null at the end
case Assignment::A_FUNCTION_RVAL:
case Assignment::A_FUNCTION_RTEMP:
case Assignment::A_EXT_FUNCTION_RVAL:
case Assignment::A_EXT_FUNCTION_RTEMP: {
Ttcn::Def_Function_Base* def_func =
dynamic_cast<Ttcn::Def_Function_Base*>(ass);
if (def_func == NULL) {
FATAL_ERROR("Type::get_field_type");
}
t = def_func->get_return_type();
if (!ref->parameters_checked()) {
Ttcn::FormalParList* fp_list = ass->get_FormalParList();
Ttcn::ParsedActualParameters* parsed_pars = ref->get_parsed_pars();
Ttcn::ActualParList* ap_list = new Ttcn::ActualParList;
bool is_erroneous = fp_list->fold_named_and_chk(parsed_pars, ap_list);
if (is_erroneous) {
delete ap_list;
return 0;
}
ap_list->set_fullname(parsed_pars->get_fullname());
ap_list->set_my_scope(parsed_pars->get_my_scope());
ref->set_actual_par_list(ap_list);
}
if (last_method != NULL) {
*last_method = ass;
}
break; }
default:
FATAL_ERROR("Type::get_field_type - %s shouldn't be in a class",
ass->get_assname());
}
break; }
default:
FATAL_ERROR("Type::get_field_type - %s shouldn't be in a class",
ass->get_assname());
}
break; }
case Ttcn::FieldOrArrayRef::ARRAY_REF: {
......@@ -6224,6 +6254,8 @@ namespace Common {
case T_ALTSTEP:
case T_TESTCASE:
return u.fatref.runs_on.type;
case T_CLASS:
return u.class_->get_RunsOnType();
default:
FATAL_ERROR("Type::get_fat_runs_on_type()");
return 0;
......@@ -7542,6 +7574,9 @@ namespace Common {
return string("COMPONENT");
case T_DEFAULT:
return string("DEFAULT");
case T_CLASS:
return t->u.class_->is_built_in() ? string("OBJECT_REF<OBJECT>") :
string("OBJECT_REF<") + t->get_genname_own(p_scope) + string(">");
case T_ARRAY:
if (!t->u.array.in_typedef)
return t->u.array.dimension->get_value_type(t->u.array.element_type,
......@@ -7634,6 +7669,11 @@ namespace Common {
const char* tn = get_typename_builtin(t->typetype);
if (tn != 0) return string(tn);
switch (t->typetype) {
case T_CLASS:
if (t->u.class_->is_built_in()) {
return string("object");
}
// else fall through
case T_COMPONENT:
case T_SIGNATURE:
case T_CHOICE_A:
......@@ -7651,7 +7691,6 @@ namespace Common {
case T_FUNCTION:
case T_ALTSTEP:
case T_TESTCASE:
case T_CLASS:
return t->get_fullname();
case T_ARRAY: {
string dimensions(t->u.array.dimension->get_stringRepr());
......
......@@ -257,6 +257,7 @@ void Type::generate_code_typedescriptor(output_struct *target)
switch (get_type_refd_last()->typetype) {
case T_PORT:
case T_SIGNATURE:
case T_CLASS:
// do not generate any type descriptor for these non-data types
return;
case T_ARRAY:
......
......@@ -2200,6 +2200,7 @@ namespace Common {
if(u.expr.v3) u.expr.v3->set_fullname(p_fullname+".<operand3>");
break;
case OPTYPE_UNDEF_CREATE: // r1 t_list2 b4
case OPTYPE_CLASS_CREATE: // r1 t_list2 b4
u.expr.r1->set_fullname(p_fullname+".<operand1>");
u.expr.t_list2->set_fullname(p_fullname+".<parameterlist>");
break;
......@@ -8216,6 +8217,9 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
u.expr.ap_list2 = parlist;
}
chk_expr_dynamic_part(exp_val, true);
my_scope->chk_runs_on_clause(t, *this, "create");
my_scope->chk_mtc_clause(t, *this);
my_scope->chk_system_clause(t, *this);
}
if (u.expr.v_optype != OPTYPE_COMP_CREATE) {
break;
......@@ -13029,6 +13033,9 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
FATAL_ERROR("Value::generate_code_init()");
}
break;
case V_TTCN3_NULL:
str = mputprintf(str, "%s = NULL_VALUE;\n", name);
break;
case V_NOTUSED:
// unbound value, don't generate anything
break;
......
......@@ -482,12 +482,19 @@ namespace Ttcn {
Free(prev_expr);
}
const Identifier& id = *ref->get_id();
Common::Assignment* ass = class_->get_local_ass_byId(id);
// 'ass' is null if the 'toString' method from the 'object' class is called
Common::Assignment* ass = class_->has_local_ass_withId(id) ?
class_->get_local_ass_byId(id) : NULL;
expr->expr = mputprintf(expr->expr, "->%s(", id.get_name().c_str());
ref->get_actual_par_list()->generate_code_noalias(expr, ass->get_FormalParList());
FormalParList* fp_list = ass != NULL ? ass->get_FormalParList() :
new FormalParList; // the formal parameter list of 'toString' is empty
ref->get_actual_par_list()->generate_code_noalias(expr, fp_list);
if (ass == NULL) {
delete fp_list;
}
expr->expr = mputc(expr->expr, ')');
Def_Function_Base* def_func = dynamic_cast<Def_Function_Base*>(ass);
type = def_func->get_return_type();
type = ass != NULL ? ass->get_Type() :
Common::Type::get_pooltype(Common::Type::T_USTR);
if (const_ref && i < n_refs - 1 &&
refs[i + 1]->get_type() != FieldOrArrayRef::FUNCTION_REF) {
// the next subreference is a field name or array index, have to
......@@ -1055,15 +1062,17 @@ namespace Ttcn {
expr->expr = mputstr(expr->expr, "this->");
}
else { // no 'id' means it's just a 'this' reference
expr->expr = mputprintf(expr->expr, "OBJECT_REF<%s>(this)",
expr->expr = mputprintf(expr->expr, "%s(this)",
ass->get_Type()->get_genname_value(my_scope).c_str());
return;
}
}
else if (reftype == REF_SUPER) {
Common::Type* base_type = my_scope->get_scope_class()->get_base_type()->
get_type_refd_last();
expr->expr = mputprintf(expr->expr, "%s::",
my_scope->get_scope_class()->get_base_type()->
get_genname_value(my_scope).c_str());
base_type->get_class_type_body()->is_built_in() ? "OBJECT" :
base_type->get_genname_own(my_scope).c_str());
}
string const_prefix; // empty by default
if (gen_const_prefix) {
......@@ -1146,12 +1155,8 @@ namespace Ttcn {
} else {
// don't convert to const object if the first subreference is a method call
if (t_subrefs->get_ref(0)->get_type() != FieldOrArrayRef::FUNCTION_REF) {
string type_str = refd_gov->get_genname_value(get_my_scope());
if (refd_gov->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) {
type_str = string("OBJECT_REF<") + type_str + string(">");
}
this_expr.expr = mputprintf(this_expr.expr, "const_cast< const %s&>(",
type_str.c_str());
refd_gov->get_genname_value(get_my_scope()).c_str());
}
}
if (parlist != NULL) {
......@@ -3688,6 +3693,11 @@ namespace Ttcn {
continue;
}
field_name = tref->get_id()->get_ttcnname();
if (oop_features && field_name == string("object")) {
ea.error("Class type `object' cannot be added to the anytype");
delete t;
continue;
}
}
else {
// Can't happen here
......@@ -3888,7 +3898,7 @@ namespace Ttcn {
break;
case Type::T_CLASS:
error("Constant cannot be defined for class type `%s'",
t->get_fullname().c_str());
t->get_typename().c_str());
break;
default:
value_under_check = true;
......@@ -4078,7 +4088,7 @@ namespace Ttcn {
break;
case Type::T_CLASS:
error("External constant cannot be defined for class type `%s'",
t->get_fullname().c_str());
t->get_typename().c_str());
break;
default:
break;
......@@ -4204,7 +4214,7 @@ namespace Ttcn {
break;
case Type::T_CLASS:
error("Type of module parameter cannot be or embed class type `%s'",
t->get_fullname().c_str());
t->get_typename().c_str());
break;
case Type::T_FUNCTION:
case Type::T_ALTSTEP:
......@@ -4384,7 +4394,7 @@ namespace Ttcn {
break;
case Type::T_CLASS:
error("Type of template module parameter cannot be class type `%s'",
t->get_fullname().c_str());
t->get_typename().c_str());
break;
default:
if (IMPLICIT_OMIT == has_implicit_omit_attr()) {
......@@ -4605,7 +4615,7 @@ namespace Ttcn {
}
else if (t->get_typetype() == Type::T_CLASS) {
error("Template cannot be defined for class type `%s'",
t->get_fullname().c_str());
t->get_typename().c_str());
}
chk_modified();
chk_recursive_derivation();
......@@ -5285,9 +5295,6 @@ namespace Ttcn {
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
string type_name = type->get_genname_value(my_scope);
if (type->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) {
type_name = string("OBJECT_REF<") + type_name + string(">");
}
if (initial_value && initial_value->has_single_expr()) {
// the initial value can be represented by a single C++ expression
// the object is initialized by the constructor
......@@ -5396,7 +5403,7 @@ namespace Ttcn {
}
else if (t->get_typetype() == Type::T_CLASS) {
error("Template variable cannot be defined for class type `%s'",
t->get_fullname().c_str());
t->get_typename().c_str());
}
if (initial_value) {
......
......@@ -2982,23 +2982,37 @@ namespace Ttcn {
// =================================
ClassTypeBody::ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final,
boolean p_abstract, Common::Type* p_base_type, Reference* p_runs_on_ref,
Reference* p_mtc_ref, Reference* p_system_ref,
boolean p_abstract, Common::Type* p_base_type,
Reference* p_runs_on_ref, Reference* p_mtc_ref, Reference* p_system_ref,
Definitions* p_members, StatementBlock* p_finally_block)
: Scope(), Location(), class_id(p_class_id), my_def(NULL), external(p_external), final(p_final), abstract(p_abstract),
base_type(p_base_type), runs_on_ref(p_runs_on_ref), runs_on_type(NULL),
mtc_ref(p_mtc_ref), mtc_type(NULL), system_ref(p_system_ref), system_type(NULL),
: Scope(), Location(), class_id(p_class_id), my_def(NULL), external(p_external), final(p_final),
abstract(p_abstract), built_in(FALSE), base_type(p_base_type),
runs_on_ref(p_runs_on_ref), runs_on_type(NULL), mtc_ref(p_mtc_ref),
mtc_type(NULL), system_ref(p_system_ref), system_type(NULL),
members(p_members), finally_block(p_finally_block), constructor(NULL), checked(false),
default_constructor(false)
{
// constructor for user-defined classes
if (members == NULL) {
FATAL_ERROR("ClassTypeBody::ClassTypeBody");
}
}
ClassTypeBody::ClassTypeBody()
: Scope(), Location(), class_id(NULL), my_def(NULL), external(FALSE), final(FALSE),
abstract(TRUE), built_in(TRUE), base_type(NULL),
runs_on_ref(NULL), runs_on_type(NULL), mtc_ref(NULL),
mtc_type(NULL), system_ref(NULL), system_type(NULL),
members(NULL), finally_block(NULL), constructor(NULL), checked(false),
default_constructor(false)
{
// constructor for the built-in class 'object'
}
ClassTypeBody::ClassTypeBody(const ClassTypeBody& p)
{
class_id = p.class_id->clone();
built_in = p.built_in;
class_id = p.class_id != NULL ? p.class_id->clone() : NULL;
my_def = p.my_def;
external = p.external;
final = p.final;
......@@ -3007,7 +3021,7 @@ namespace Ttcn {
runs_on_ref = p.runs_on_ref != NULL ? p.runs_on_ref->clone() : NULL;
mtc_ref = p.mtc_ref != NULL ? p.mtc_ref->clone() : NULL;
system_ref = p.system_ref != NULL ? p.system_ref->clone() : NULL;
members = p.members->clone();
members = p.members != NULL ? p.members->clone() : NULL;
finally_block = p.finally_block != NULL ? p.finally_block->clone() : NULL;
default_constructor = p.default_constructor;
constructor = default_constructor ? p.constructor->clone() : p.constructor;
......@@ -3021,6 +3035,9 @@ namespace Ttcn {
ClassTypeBody::~ClassTypeBody()
{
if (built_in) {
return;
}
delete base_type;
delete finally_block;
delete members;
......@@ -3034,6 +3051,9 @@ namespace Ttcn {
void ClassTypeBody::set_fullname(const string& p_fullname)
{
if (built_in) {
return;
}
Common::Scope::set_fullname(p_fullname);
if (base_type != NULL) {
base_type->set_fullname(p_fullname + ".<superclass>");
......@@ -3055,6 +3075,9 @@ namespace Ttcn {
void ClassTypeBody::set_my_scope(Scope* p_scope)
{
if (built_in) {
return;
}
set_parent_scope(p_scope);
if (base_type != NULL) {
base_type->set_my_scope(p_scope);
......@@ -3076,6 +3099,10 @@ namespace Ttcn {
void ClassTypeBody::dump(unsigned level) const
{
if (built_in) {
DEBUG(level, "Built-in class 'object'");
return;
}
DEBUG(level, "Modifiers:%s%s%s", external ? "external " : "",
final ? " @final " : "", abstract ? " @abstract" : "");
DEBUG(level, "Base class: %s", base_type != NULL ?
......@@ -3132,7 +3159,7 @@ namespace Ttcn {
bool ClassTypeBody::is_parent_class(const ClassTypeBody* p_class) const
{
if (this == p_class) {
if (this == p_class || p_class->built_in) {
return true;
}
if (base_type == NULL) {
......@@ -3144,6 +3171,9 @@ namespace Ttcn {
bool ClassTypeBody::has_local_ass_withId(const Identifier& p_id)
{
if (built_in) {
return false;
}
chk();
if (members->has_local_ass_withId(p_id)) {
return true;
......@@ -3159,6 +3189,9 @@ namespace Ttcn {
Common::Assignment* ClassTypeBody::get_local_ass_byId(const Identifier& p_id)
{
if (built_in) {