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)
{
......@@ -752,6 +783,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,11 +1781,35 @@ namespace Common {
return 0;
}
Ttcn::ClassTypeBody* class_ = t->get_class_type_body();
bool base_toString = false;
if (!class_->has_local_ass_withId(id)) {
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;
}
}
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)
......@@ -1837,6 +1866,7 @@ namespace Common {
FATAL_ERROR("Type::get_field_type - %s shouldn't be in a class",
ass->get_assname());
}
}
break; }
case Ttcn::FieldOrArrayRef::ARRAY_REF: {
Type *embedded_type = 0;
......@@ -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) {
FATAL_ERROR("ClassTypeBody::get_local_ass_byId");
}
chk();
Common::Assignment* ass = NULL;
if (members->has_local_ass_withId(p_id)) {
......@@ -3175,6 +3208,9 @@ namespace Ttcn {
Common::Location* usage_loc,
Common::Scope* usage_scope)
{
if (built_in) {
FATAL_ERROR("ClassTypeBody::chk_visibility");
}
if (ass->get_visibility() == PUBLIC) {
// it's public, so it doesn't matter where it's accessed from
return true;
......@@ -3204,6 +3240,9 @@ namespace Ttcn {
Common::Assignment* ClassTypeBody::get_ass_bySRef(Common::Ref_simple* p_ref)
{
if (built_in) {
return NULL;
}
if (p_ref == NULL || parent_scope == NULL) {
FATAL_ERROR("ClassTypeBody::get_ass_bySRef()");
}
......@@ -3251,7 +3290,7 @@ namespace Ttcn {
void ClassTypeBody::chk()
{
if (checked) {
if (checked || built_in) {
return;
}
checked = true;
......@@ -3270,20 +3309,64 @@ namespace Ttcn {
// TODO: additional checks for the base type
}
ClassTypeBody* base_class = base_type != NULL ?
base_type->get_type_refd_last()->get_class_type_body() : NULL;
if (runs_on_ref != NULL) {
Error_Context cntxt(runs_on_ref, "In `runs on' clause");
runs_on_type = runs_on_ref->chk_comptype_ref();
// TODO
if (base_class != NULL) {
Type* base_runs_on_type = base_class->get_RunsOnType();
if (base_runs_on_type != NULL &&
!base_runs_on_type->is_compatible(runs_on_type, NULL, NULL)) {
runs_on_ref->error("The `runs on' component type of the subclass, "
"`%s', is not compatible with the `runs on' component type of the "
"superclass, `%s'",
runs_on_type->get_typename().c_str(),
base_runs_on_type->get_typename().c_str());
}
}
}
else if (base_class != NULL) {
// inherit `runs on' component from the superclass
runs_on_type = base_class->get_RunsOnType();
}
if (mtc_ref != NULL) {
Error_Context cntxt(mtc_ref, "In `mtc' clause");
mtc_type = mtc_ref->chk_comptype_ref();
// TODO
if (base_class != NULL) {
Type* base_mtc_type = base_class->get_MtcType();
if (base_mtc_type != NULL &&
!base_mtc_type->is_compatible(mtc_type, NULL, NULL)) {
mtc_ref->error("The `mtc' component type of the subclass, `%s', is not "
"compatible with the `mtc' component type of the superclass, `%s'",
mtc_type->get_typename().c_str(),
base_mtc_type->get_typename().c_str());
}
}
}
else if (base_class != NULL) {
// inherit `mtc' component from the superclass
mtc_type = base_class->get_MtcType();
}
if (system_ref != NULL) {
Error_Context cntxt(system_ref, "In `system' clause");
system_type = system_ref->chk_comptype_ref();
// TODO
if (base_class != NULL) {
Type* base_system_type = base_class->get_SystemType();
if (base_system_type != NULL &&
!base_system_type->is_compatible(system_type, NULL, NULL)) {
system_ref->error("The `system' component type of the subclass, `%s', is "
"not compatible with the `system' component type of the superclass, `%s'",
system_type->get_typename().c_str(),
base_system_type->get_typename().c_str());
}
}
}
else if (base_class != NULL) {
// inherit `system' component from the superclass
system_type = base_class->get_SystemType();
}
members->chk_uniq();
......@@ -3305,9 +3388,7 @@ namespace Ttcn {
// create a default constructor
Reference* base_call = NULL;
FormalParList* fp_list = NULL;
if (