-
Botond Baranyi authored
OOP: fixed name clash in the constructor and improved previous modifications to work with subreferences (issue #584) Signed-off-by:
Botond Baranyi <botond.baranyi@ericsson.com>
Botond Baranyi authoredOOP: fixed name clash in the constructor and improved previous modifications to work with subreferences (issue #584) Signed-off-by:
Botond Baranyi <botond.baranyi@ericsson.com>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
AST_ttcn3.cc 447.86 KiB
/******************************************************************************
* Copyright (c) 2000-2021 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Baji, Laszlo
* Balasko, Jeno
* Baranyi, Botond
* Beres, Szabolcs
* Delic, Adam
* Knapp, Adam
* Kovacs, Ferenc
* Raduly, Csaba
* Szabados, Kristof
* Szabo, Bence Janos
* Szalai, Gabor
* Zalanyi, Balazs Andor
* Pandi, Krisztian
*
******************************************************************************/
#include "../../common/dbgnew.hh"
#include "../../common/version.h"
#include "AST_ttcn3.hh"
#include "../Identifier.hh"
#include "../CompilerError.hh"
#include "../Setting.hh"
#include "../Type.hh"
#include "../CompField.hh"
#include "../CompType.hh"
#include "../TypeCompat.hh"
#include "../Valuestuff.hh"
#include "../Value.hh"
#include "Ttcnstuff.hh"
#include "TtcnTemplate.hh"
#include "Templatestuff.hh"
#include "ArrayDimensions.hh"
#include "compiler.h"
#include "../main.hh"
#include "Statement.hh"
#include "ILT.hh"
#include "Attributes.hh"
#include "PatternString.hh"
#include "../../common/version_internal.h"
#include "../CodeGenHelper.hh"
#include "../../common/JSON_Tokenizer.hh"
#include "../DebuggerStuff.hh"
#include "../SigParam.hh"
#include <limits.h>
// implemented in coding_attrib_p.y
extern Ttcn::ExtensionAttributes * parse_extattributes(
Ttcn::WithAttribPath *w_attrib_path);
// implemented in compiler.y
extern Ttcn::ErroneousAttributeSpec* ttcn3_parse_erroneous_attr_spec_string(
const char* p_str, const Common::Location& str_loc);
extern void init_coding_attrib_lex(const Ttcn::AttributeSpec& attrib);
extern int coding_attrib_parse();
extern void cleanup_coding_attrib_lex();
extern Ttcn::ExtensionAttributes *extatrs;
/** Create a field name in the anytype
*
* The output of this function will be used to create an identifier
* to be used as the field name in the anytype.
* The type_name may be a built-in type (e.g. "integer") or a user-defined
* type.
*
* If the name has multiple components (a fullname?), it keeps just the last
* component without any dots. *
* Also, the space in "universal charstring" needs to be replaced
* with an underscore to make it an identifier.
*
* Note: Prefixing with "AT_" is not done here, but in defUnionClass().
*
* @param type_name string
* @return string to be used as the identifier.
*/
string anytype_field(const string& type_name)
{
string retval(type_name);
// keep just the last part of the name
// TODO check if there's a way to get just the last component (note that fetching the string is done outside of this function)
size_t dot = retval.rfind('.');
if (dot >= retval.size()) dot = 0;
else ++dot;
retval.replace(0, dot, "");
return retval;
}
extern Common::Modules *modules; // in main.cc
namespace {
static const string _T_("_T_");
}
namespace Ttcn {
using namespace Common;
// =================================
// ===== FieldOrArrayRef
// =================================
FieldOrArrayRef::FieldOrArrayRef(const FieldOrArrayRef& p)
: Node(p), Location(p), ref_type(p.ref_type)
{
switch (p.ref_type) {
case FIELD_REF:
case FUNCTION_REF:
u.ff.id = p.u.ff.id->clone();
u.ff.checked = p.u.ff.checked;
if (u.ff.checked) {
u.ff.ap_list = p.u.ff.ap_list != NULL ? p.u.ff.ap_list->clone() : NULL;
}
else {
u.ff.parsed_pars = p.u.ff.parsed_pars != NULL ? p.u.ff.parsed_pars->clone() : NULL;
}
break;
case ARRAY_REF:
u.arp = p.u.arp->clone();
break;
default:
FATAL_ERROR("FieldOrArrayRef::FieldOrArrayRef()");
}
}
FieldOrArrayRef::FieldOrArrayRef(Identifier *p_id,
ParsedActualParameters* p_params /* = NULL */)
: Node(), Location(), ref_type(p_params != NULL ? FUNCTION_REF : FIELD_REF)
{
if (!p_id) FATAL_ERROR("FieldOrArrayRef::FieldOrArrayRef()");
u.ff.id = p_id;
u.ff.parsed_pars = p_params;
u.ff.checked = false;
}
FieldOrArrayRef::FieldOrArrayRef(Value *p_arp)
: Node(), Location(), ref_type(ARRAY_REF)
{
if (!p_arp) FATAL_ERROR("FieldOrArrayRef::FieldOrArrayRef()");
u.arp = p_arp;
}
FieldOrArrayRef::~FieldOrArrayRef()
{
switch (ref_type) {
case FIELD_REF:
case FUNCTION_REF:
delete u.ff.id;
if (u.ff.checked) {
delete u.ff.ap_list;
}
else {
delete u.ff.parsed_pars;
}
break;
case ARRAY_REF:
delete u.arp;
break;
default:
FATAL_ERROR("FieldOrArrayRef::~FieldOrArrayRef()");
}
}
FieldOrArrayRef *FieldOrArrayRef::clone() const
{
return new FieldOrArrayRef(*this);
}
void FieldOrArrayRef::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
if (ref_type == ARRAY_REF)
u.arp->set_fullname(p_fullname + ".<array_index>");
else if (ref_type == FUNCTION_REF) {
if (u.ff.checked) {
u.ff.ap_list->set_fullname(p_fullname + ".<parameterlist>");
}
else {
u.ff.parsed_pars->set_fullname(p_fullname + ".<parameterlist>");
}
}
}
void FieldOrArrayRef::set_my_scope(Scope *p_scope)
{
if (ref_type == ARRAY_REF) u.arp->set_my_scope(p_scope);
else if (ref_type == FUNCTION_REF) {
if (u.ff.checked) {
u.ff.ap_list->set_my_scope(p_scope);
}
else {
u.ff.parsed_pars->set_my_scope(p_scope);
}
}
}
const Identifier* FieldOrArrayRef::get_id() const
{
if (ref_type == ARRAY_REF) FATAL_ERROR("FieldOrArrayRef::get_id()");
return u.ff.id;
}
Value *FieldOrArrayRef::get_val() const
{
if (ref_type != ARRAY_REF) FATAL_ERROR("FieldOrArrayRef::get_val()");
return u.arp;
}
ParsedActualParameters* FieldOrArrayRef::get_parsed_pars() const
{
if (ref_type != FUNCTION_REF || u.ff.checked) {
FATAL_ERROR("FieldOrArrayRef::get_parsed_pars()");
}
return u.ff.parsed_pars;
}
bool FieldOrArrayRef::parameters_checked() const
{
if (ref_type != FUNCTION_REF) {
FATAL_ERROR("FieldOrArrayRef::parameters_checked()");
}
return u.ff.checked;
}
ActualParList* FieldOrArrayRef::get_actual_par_list() const
{
if (ref_type != FUNCTION_REF || !u.ff.checked) {
FATAL_ERROR("FieldOrArrayRef::get_actual_par_list()");
}
return u.ff.ap_list;
}
FormalParList* FieldOrArrayRef::get_formal_par_list() const
{
if (ref_type != FUNCTION_REF || !u.ff.checked) {
FATAL_ERROR("FieldOrArrayRef::get_formal_par_list()");
}
return u.ff.fp_list;
}
void FieldOrArrayRef::set_parameter_list(ActualParList* p_ap_list, FormalParList* p_fp_list)
{
if (ref_type != FUNCTION_REF || u.ff.checked) {
FATAL_ERROR("FieldOrArrayRef::set_parameter_list()");
}
u.ff.checked = true;
delete u.ff.parsed_pars;
u.ff.ap_list = p_ap_list;
u.ff.fp_list = p_fp_list;
}
void FieldOrArrayRef::append_stringRepr(string& str) const
{
switch (ref_type) {
case FIELD_REF:
case FUNCTION_REF:
str += '.';
str += u.ff.id->get_dispname();
if (ref_type == FUNCTION_REF) {
size_t nof_pars = u.ff.checked ? u.ff.ap_list->get_nof_pars() :
u.ff.parsed_pars->get_nof_tis();
if (nof_pars > 0) {
str += '(';
for (size_t i = 0; i < nof_pars; ++i) {
if (i > 0) {
str += ", ";
}
if (u.ff.checked) {
u.ff.ap_list->get_par(i)->append_stringRepr(str);
}
else {
u.ff.parsed_pars->get_ti_byIndex(i)->append_stringRepr(str);
}
}
str += ')';
}
}
break;
case ARRAY_REF:
str += '[';
str += u.arp->get_stringRepr();
str += ']';
break;
default:
str += "<unknown sub-reference>";
}
}
void FieldOrArrayRef::set_field_name_to_lowercase()
{
if (ref_type != FIELD_REF) FATAL_ERROR("FieldOrArrayRef::set_field_name_to_lowercase()");
string new_name = u.ff.id->get_name();
if (isupper(new_name[0])) {
new_name[0] = static_cast<char>( tolower(new_name[0] ));
if (new_name[new_name.size() - 1] == '_') {
// an underscore is inserted at the end of the field name if it's
// a basic type's name (since it would conflict with the class generated
// for that type)
// remove the underscore, it won't conflict with anything if its name
// starts with a lowercase letter
new_name.replace(new_name.size() - 1, 1, "");
}
delete u.ff.id;
u.ff.id = new Identifier(Identifier::ID_NAME, new_name);
}
}
// =================================
// ===== FieldOrArrayRefs
// =================================
FieldOrArrayRefs::FieldOrArrayRefs(const FieldOrArrayRefs& p)
: Node(p), refs_str_element(false), my_scope(NULL), checked(false)
{
for (size_t i = 0; i < p.refs.size(); i++) refs.add(p.refs[i]->clone());
}
FieldOrArrayRefs::~FieldOrArrayRefs()
{
for (size_t i = 0; i < refs.size(); i++) delete refs[i];
refs.clear();
}
FieldOrArrayRefs *FieldOrArrayRefs::clone() const
{
return new FieldOrArrayRefs(*this);
}
void FieldOrArrayRefs::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
for (size_t i = 0; i < refs.size(); i++)
refs[i]->set_fullname(p_fullname +
".<sub_reference" + Int2string(i + 1) + ">");
}
void FieldOrArrayRefs::set_my_scope(Scope *p_scope)
{
my_scope = p_scope;
for (size_t i = 0; i < refs.size(); i++) refs[i]->set_my_scope(p_scope);
}
bool FieldOrArrayRefs::has_unfoldable_index() const
{
for (size_t i = 0; i < refs.size(); i++) {
FieldOrArrayRef *ref = refs[i];
if (ref->get_type() == FieldOrArrayRef::ARRAY_REF) {
Value *v = ref->get_val();
v->set_lowerid_to_ref();
if (v->is_unfoldable()) return true;
}
}
return false;
}
void FieldOrArrayRefs::remove_refs(size_t n)
{
for (size_t i = 0; i < n; i++) delete refs[i];
refs.replace(0, n, NULL);
set_fullname(get_fullname());
}
/* remove_last_field is used when unfolding references for
ischosen and ispresent function operands.
In this case it is NOT sure the last field exists.
Calling remove_last_field previously
will avoid getting the "variable...Has no member called..." error message.
The last field component will be checked as a separate step.
Warning: the removed Identifier has to be deleted later */
Identifier* FieldOrArrayRefs::remove_last_field()
{
if (refs.size() == 0) return 0;
size_t last_elem_ind = refs.size() - 1;
FieldOrArrayRef* last_elem = refs[last_elem_ind];
if (last_elem->get_type() == FieldOrArrayRef::FIELD_REF) {
Identifier *ret_val = last_elem->get_id()->clone();
delete last_elem;
refs.replace(last_elem_ind, 1, NULL);
return ret_val;
} else return 0;
}
void FieldOrArrayRefs::generate_code(expression_struct *expr,
Common::Assignment *ass, Common::Scope* ref_scope,
bool const_ref /* = false */, size_t nof_subrefs /* = UINT_MAX*/)
{
Type *type = 0;
bool is_template = false;
switch (ass->get_asstype()) {
case Common::Assignment::A_CONST: // a Def_Const
case Common::Assignment::A_EXT_CONST: // a Def_ExtConst
case Common::Assignment::A_MODULEPAR: // a Def_Modulepar
case Common::Assignment::A_VAR: // a Def_Var
case Common::Assignment::A_EXCEPTION: // a Def_Exception
case Common::Assignment::A_FUNCTION_RVAL: // a Def_Function
case Common::Assignment::A_EXT_FUNCTION_RVAL: // a Def_ExtFunction
case Common::Assignment::A_PAR_VAL_IN: // a FormalPar
case Common::Assignment::A_PAR_VAL_OUT: // a FormalPar
case Common::Assignment::A_PAR_VAL_INOUT: // a FormalPar
// The type is important since the referred entities are value objects.
type = ass->get_Type();
break;
case Common::Assignment::A_MODULEPAR_TEMP: // a Def_Modulepar_Template
case Common::Assignment::A_TEMPLATE: // a Def_Template
case Common::Assignment::A_VAR_TEMPLATE: // a Def_Var_Template
case Common::Assignment::A_PAR_TEMPL_IN: // a FormalPar
case Common::Assignment::A_PAR_TEMPL_OUT: // a FormalPar
case Common::Assignment::A_PAR_TEMPL_INOUT: // a FormalPar
// The type is semi-important because fields of anytype templates
// need the prefix.
type = ass->get_Type();
is_template = true;
break;
case Common::Assignment::A_TIMER: // a Def_Timer
case Common::Assignment::A_PORT: // a Def_Port
case Common::Assignment::A_FUNCTION_RTEMP: // a Def_Function
case Common::Assignment::A_EXT_FUNCTION_RTEMP: // a Def_ExtFunction
case Common::Assignment::A_PAR_TIMER: // a FormalPar
case Common::Assignment::A_PAR_PORT: // a FormalPar
// The type is not relevant (i.e. the optional fields do not require
// special handling).
type = 0;
break;
default:
// Reference to other definitions cannot occur during code generation.
FATAL_ERROR("FieldOrArrayRefs::generate_code()");
type = 0;
}
generate_code(expr, type, ref_scope, const_ref, is_template, nof_subrefs);
}
void FieldOrArrayRefs::generate_code(expression_struct* expr, Type* type,
Common::Scope* ref_scope,
bool const_ref, /* = false */
bool is_template, /* = false */
size_t nof_subrefs /* = UINT_MAX */)
{
size_t n_refs = (nof_subrefs != UINT_MAX) ? nof_subrefs : refs.size();
for (size_t i = 0; i < n_refs; i++) {
if (type) type = type->get_type_refd_last();
// type changes inside the loop; need to recompute "last" every time.
FieldOrArrayRef *ref = refs[i];
if (ref->get_type() == FieldOrArrayRef::FIELD_REF) {
// Write a call to the field accessor method.
// Fields of the anytype get a special prefix; see also:
// 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();
}
}
} else if (ref->get_type() == FieldOrArrayRef::FUNCTION_REF) {
if (type == NULL) {
FATAL_ERROR("FieldOrArrayRefs::generate_code");
}
ClassTypeBody* class_ = type->get_class_type_body();
if (const_ref && i > 0 &&
refs[i - 1]->get_type() != FieldOrArrayRef::FUNCTION_REF) { // todo: convert if previous type wasn't a class
// all class methods are non-const, have to convert the current object
// to non-const
char* prev_expr = expr->expr;
expr->expr = mprintf("const_cast< %s&>(%s)",
type->get_genname_value(ref_scope).c_str(), prev_expr);
Free(prev_expr);
}
const Identifier& id = *ref->get_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());
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, ')');
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
// convert the resulting object back to const
// (use static_cast, since methods return temporary objects, not
// references)
char* prev_expr = expr->expr;
expr->expr = mprintf("static_cast<const %s>(%s)",
type->get_genname_value(ref_scope).c_str(), prev_expr);
Free(prev_expr);
}
} else {
// Generate code for array reference.
Value* v = ref->get_val();
Type * pt = v->get_expr_governor_last();
// If the value is indexed with an array or record of then generate
// the indexes of the array or record of into the code, one by one.
if (pt->get_typetype() == Type::T_ARRAY || pt->get_typetype() == Type::T_SEQOF) {
int len = 0, start = 0;
if (pt->get_typetype() == Type::T_ARRAY) {
len = static_cast<int>( pt->get_dimension()->get_size() );
start = pt->get_dimension()->get_offset();
} else if (pt->get_typetype() == Type::T_SEQOF) {
len = pt->get_sub_type()->get_length_restriction();
}
// Generate the indexes as [x][y]...
for (int j = start; j < start + len; j++) {
expr->expr = mputc(expr->expr, '[');
v->generate_code_expr(expr);
expr->expr = mputprintf(expr->expr, "[%i]]", j);
}
} else {
expr->expr = mputc(expr->expr, '[');
v->generate_code_expr(expr);
expr->expr = mputc(expr->expr, ']');
}
if (type) {
// Follow the embedded type.
switch (type->get_typetype()) {
case Type::T_SEQOF:
case Type::T_SETOF:
case Type::T_ARRAY:
type = type->get_ofType();
break;
default:
// The index points to a string element.
// There are no further sub-references.
type = 0;
} // switch
} // if (type)
} // if (ref->get_type)
} // next reference
}
void FieldOrArrayRefs::append_stringRepr(string& str) const
{
for (size_t i = 0; i < refs.size(); i++) refs[i]->append_stringRepr(str);
}
bool FieldOrArrayRefs::has_function_ref() const
{
for (size_t i = 0; i < refs.size(); ++i) {
if (refs[i]->get_type() == FieldOrArrayRef::FUNCTION_REF) {
return true;
}
}
return false;
}
void FieldOrArrayRefs::use_default_alternative(size_t p_idx, const Identifier& p_alt_name)
{
FieldOrArrayRef* alt_ref = new FieldOrArrayRef(new Identifier(p_alt_name));
alt_ref->set_my_scope(my_scope);
refs.insert(alt_ref, p_idx);
set_fullname(get_fullname());
}
// =================================
// ===== Ref_base
// =================================
Ref_base::Ref_base(const Ref_base& p)
: Ref_simple(p), subrefs(p.subrefs)
{
modid = p.modid ? p.modid->clone() : 0;
id = p.id ? p.id->clone() : 0;
params_checked = p.params_checked;
}
Ref_base::Ref_base(Identifier *p_modid, Identifier *p_id)
: Ref_simple(), modid(p_modid), id(p_id), params_checked(false)
, usedInIsbound(false)
{
if (!p_id)
FATAL_ERROR("NULL parameter: Ttcn::Ref_base::Ref_base()");
}
Ref_base::~Ref_base()
{
delete modid;
delete id;
}
void Ref_base::set_fullname(const string& p_fullname)
{
Ref_simple::set_fullname(p_fullname);
subrefs.set_fullname(p_fullname);
}
void Ref_base::set_my_scope(Scope *p_scope)
{
Ref_simple::set_my_scope(p_scope);
subrefs.set_my_scope(p_scope);
}
/* returns the referenced variable's base type or value */
Setting* Ref_base::get_refd_setting()
{
Common::Assignment *ass = get_refd_assignment();
if (ass) return ass->get_Setting();
else return 0;
}
FieldOrArrayRefs *Ref_base::get_subrefs()
{
if (!id) get_modid();
if (subrefs.get_nof_refs() == 0) return 0;
else return &subrefs;
}
bool Ref_base::has_single_expr()
{
Common::Assignment *ass = get_refd_assignment();
if (!ass) FATAL_ERROR("Ref_base::has_single_expr()");
for (size_t i = 0; i < subrefs.get_nof_refs(); i++) {
FieldOrArrayRef *ref = subrefs.get_ref(i);
if (ref->get_type() == FieldOrArrayRef::ARRAY_REF &&
!ref->get_val()->has_single_expr()) return false;
}
return true;
}
void Ref_base::set_code_section(
GovernedSimple::code_section_t p_code_section)
{
for (size_t i = 0; i < subrefs.get_nof_refs(); i++) {
FieldOrArrayRef *ref = subrefs.get_ref(i);
if (ref->get_type() == FieldOrArrayRef::ARRAY_REF)
ref->get_val()->set_code_section(p_code_section);
}
}
void Ref_base::chk_immutability()
{
Common::Assignment* ass = get_refd_assignment();
switch (ass->get_asstype()) {
case Common::Assignment::A_TYPE: /**< type */
case Common::Assignment::A_CONST: /**< value (const) */
case Common::Assignment::A_UNDEF: /**< undefined/undecided (ASN.1) */
case Common::Assignment::A_ERROR: /**< erroneous; the kind cannot be deduced (ASN.1) */
case Common::Assignment::A_OC: /**< information object class (ASN.1) */
case Common::Assignment::A_OBJECT: /**< information object (ASN.1) */
case Common::Assignment::A_OS: /**< information object set (ASN.1) */
case Common::Assignment::A_VS: /**< value set (ASN.1) */
case Common::Assignment::A_EXT_CONST: /**< external constant (TTCN-3) */
case Common::Assignment::A_MODULEPAR: /**< module parameter (TTCN-3) */
case Common::Assignment::A_MODULEPAR_TEMP: /**< template module parameter */
case Common::Assignment::A_VAR: /**< variable (TTCN-3) */
case Common::Assignment::A_EXCEPTION: /**< exception (TTCN-3) */
case Common::Assignment::A_VAR_TEMPLATE: /**< template variable: dynamic template (TTCN-3) */
case Common::Assignment::A_TIMER: /**< timer (TTCN-3) */
case Common::Assignment::A_PORT: /**< port (TTCN-3) */
case Common::Assignment::A_ALTSTEP: /**< altstep (TTCN-3) */
case Common::Assignment::A_TESTCASE: /**< testcase Assignment::(TTCN-3) */
case Common::Assignment::A_PAR_TIMER: /**< formal parameter (timer) (TTCN-3) */
case Common::Assignment::A_PAR_PORT: /**< formal parameter (port) (TTCN-3) */
break;
case Common::Assignment::A_TEMPLATE:
if (get_parlist() != NULL) {
get_parlist()->chk_immutability();
}
break;
case Common::Assignment::A_FUNCTION: /**< function without return type (TTCN-3) */
case Common::Assignment::A_FUNCTION_RVAL: /**< function that returns a value (TTCN-3) */
case Common::Assignment::A_FUNCTION_RTEMP: /**< function that returns a template (TTCN-3) */
case Common::Assignment::A_EXT_FUNCTION: /**< external function without return type (TTCN-3) */
case Common::Assignment::A_EXT_FUNCTION_RVAL: /**< ext. func that returns a value (TTCN-3) */
case Common::Assignment::A_EXT_FUNCTION_RTEMP: /**< ext. func that returns a template (TTCN-3) */
warning("Function invocation '%s' may change the actual snapshot.",
get_dispname().c_str());
break;
case Common::Assignment::A_PAR_VAL: /**< formal parameter (value) (TTCN-3) */
case Common::Assignment::A_PAR_VAL_IN: /**< formal parameter (in value) (TTCN-3) */
case Common::Assignment::A_PAR_VAL_OUT: /**< formal parameter (out value) (TTCN-3) */
// TODO: @fuzzy INOUT parameter is not valid
case Common::Assignment::A_PAR_VAL_INOUT: /**< formal parameter (inout value) (TTCN-3) */
case Common::Assignment::A_PAR_TEMPL_IN: /**< formal parameter ([in] template) (TTCN-3) */
case Common::Assignment::A_PAR_TEMPL_OUT: /**< formal parameter (out template) (TTCN-3) */
case Common::Assignment::A_PAR_TEMPL_INOUT:/**< formal parameter (inout template) (TTCN-3) */
if (ass->get_eval_type() == FUZZY_EVAL) {
warning("Fuzzy parameter '%s' may change (during) the actual snapshot.",
get_dispname().c_str());
}
break;
default:
FATAL_ERROR("Ref_base::chk_immutability()");
}
}
void Ref_base::generate_code_const_ref(expression_struct_t */*expr*/)
{
FATAL_ERROR("Ref_base::generate_code_const_ref()");
}
// =================================
// ===== Reference
// =================================
Reference::Reference(const Reference& p)
: Ref_base(p), reftype(p.reftype), gen_const_prefix(false),
expr_cache(NULL)
{
params = p.params != NULL ? p.params->clone() : NULL;
parlist = p.parlist != NULL ? p.parlist->clone() : NULL;
}
Reference::Reference(Identifier *p_id)
: Ref_base(), reftype(REF_BASIC), parlist(NULL), params(NULL),
gen_const_prefix(false), expr_cache(NULL),
gen_class_defpar_prefix(false), gen_class_base_call_postfix(false)
{
subrefs.add(new FieldOrArrayRef(p_id));
}
Reference::Reference(reftype_t p_reftype)
: Ref_base(), reftype(p_reftype), parlist(NULL), params(NULL),
gen_const_prefix(false), expr_cache(NULL),
gen_class_defpar_prefix(false), gen_class_base_call_postfix(false)
{
if (reftype != REF_THIS && reftype != REF_VALUE) {
FATAL_ERROR("Ttcn::Reference(): basic or 'super' reference with no ID");
}
}
Reference::Reference(Identifier *p_modid, Identifier *p_id,
ParsedActualParameters *p_params, reftype_t p_reftype)
: Ref_base(p_modid, p_id), reftype(p_reftype), parlist(NULL), params(p_params),
gen_const_prefix(false), expr_cache(NULL),
gen_class_defpar_prefix(false), gen_class_base_call_postfix(false)
{
if (p_params == NULL) {
FATAL_ERROR("Ttcn::Reference::Reference(): NULL parameter");
}
}
Reference::~Reference()
{
delete parlist;
delete params;
Free(expr_cache);
}
bool Reference::has_parameters() const
{
return params_checked ? parlist != NULL : params != NULL;
}
/* Called by:
* Common::PortTypeBody::PortTypeBody
* Common::Type::Type
* Common::TypeMappingTarget::TypeMappingTarget
* Common::PatternString::ps_elem_t::chk_ref */
Reference *Reference::clone() const
{
return new Reference(*this);
}
void Reference::set_fullname(const string& p_fullname)
{
Ref_base::set_fullname(p_fullname);
if (parlist != NULL) {
parlist->set_fullname(p_fullname);
}
if (params != NULL) {
params->set_fullname(p_fullname);
}
}
void Reference::set_my_scope(Scope *p_scope)
{
Ref_base::set_my_scope(p_scope);
if (parlist != NULL) {
parlist->set_my_scope(p_scope);
}
if (params != NULL) {
params->set_my_scope(p_scope);
}
}
string Reference::get_dispname()
{
if (is_erroneous) return string("erroneous");
string ret_val;
if (!has_parameters()) { // TODO: simplify
if (id) {
if (modid) {
ret_val += modid->get_dispname();
ret_val += '.';
}
ret_val += id->get_dispname();
subrefs.append_stringRepr(ret_val);
} else {
subrefs.append_stringRepr(ret_val);
// cut the leading dot
if (!ret_val.empty() && ret_val[0] == '.')
ret_val.replace(0, 1, "");
}
}
else { // has_parameters()
if (modid) {
ret_val += modid->get_dispname();
ret_val += '.';
}
ret_val += id->get_dispname();
ret_val += '(';
if (params_checked) {
// used after semantic analysis
for (size_t i = 0; i < parlist->get_nof_pars(); i++) {
if (i > 0) ret_val += ", ";
parlist->get_par(i)->append_stringRepr(ret_val);
}
} else {
// used before semantic analysis
for (size_t i = 0; i < params->get_nof_tis(); i++) {
if (i > 0) ret_val += ", ";
params->get_ti_byIndex(i)->append_stringRepr(ret_val);
}
}
ret_val += ')';
subrefs.append_stringRepr(ret_val);
}
return ret_val;
}
Common::Assignment* Reference::get_refd_assignment(bool check_parlist)
{
StatementBlock* sb = my_scope != NULL ? my_scope->get_statementblock_scope() : NULL;
Common::Assignment *ass = NULL;
if (reftype == REF_VALUE) {
if (sb != NULL && sb->is_in_dynamic_template()) {
ass = sb->get_dynamic_template()->get_dynamic_formalpar();
}
else {
error("Reference to value being matched is only allowed inside a "
"dynamic template's statement block");
}
}
else {
ass = Ref_base::get_refd_assignment(check_parlist);
// In fact calls Ref_simple::get_refd_assignment
}
if (ass && check_parlist && !params_checked) {
params_checked = true;
if (params == NULL) {
// tricky: ass->get_FormalParList() can delete this object (e.x. in case of
// erroneous recursive templates), avoid touching members for as long as
// possible...
FormalParList *fplist = ass->get_FormalParList();
if (fplist != NULL) {
if (fplist->has_only_default_values() &&
Common::Assignment::A_TEMPLATE == ass->get_asstype()) {
Ttcn::ParsedActualParameters dummy_params;
Error_Context cntxt(&dummy_params, "In actual parameter list of %s",
ass->get_description().c_str());
parlist = new ActualParList();
is_erroneous = fplist->fold_named_and_chk(&dummy_params, parlist);
parlist->set_fullname(get_fullname());
parlist->set_my_scope(my_scope);
} else {
error("Reference to parameterized definition `%s' without "
"actual parameter list", ass->get_id().get_dispname().c_str());
}
}
}
else { // params != NULL
if (ass->get_asstype() == Common::Assignment::A_TYPE) {
Def_Type* def = dynamic_cast<Def_Type*>(ass);
if (def == NULL) {
FATAL_ERROR("Reference::get_refd_assignment");
}
Type* type = def->get_Type();
if (type->get_typetype() == Common::Type::T_CLASS) {
// if the referred assignment is a class type, then the reference and
// its parameters are meant for the constructor instead
ass = type->get_class_type_body()->get_constructor();
if (ass == NULL) {
return NULL;
}
}
}
FormalParList* fplist = ass->get_FormalParList();
if (fplist != NULL) {
Error_Context cntxt(params, "In actual parameter list of %s",
ass->get_description().c_str());
parlist = new ActualParList();
is_erroneous = fplist->fold_named_and_chk(params, parlist);
parlist->set_fullname(get_fullname());
parlist->set_my_scope(my_scope);
// the parsed parameter list is no longer needed
delete params;
params = NULL;
} else {
params->error("The referenced %s cannot have actual parameters",
ass->get_description().c_str());
}
}
}
if (ass != NULL) {
ref_usage_found(ass);
if (sb != NULL && sb->is_in_finally_block()) {
switch (ass->get_asstype()) {
default:
if (!ass->is_local()) {
break;
}
// else fall through
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
sb->get_finally_block()->add_refd_local_def(ass);
}
}
}
return ass;
}
Common::Assignment* Reference::get_refd_assignment_last(bool check_parlist)
{
Common::Assignment* ass = get_refd_assignment(check_parlist);
if (ass != NULL && subrefs.get_nof_refs() != 0) {
switch (ass->get_asstype()) {
case Common::Assignment::A_VAR:
case Common::Assignment::A_EXCEPTION:
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_VAL_OUT: {
// there could be class objects in the subreferences, which would change
// the type of the last assignment
Type* type = ass->get_Type();
if (type->get_field_type(&subrefs, Common::Type::EXPECTED_DYNAMIC_VALUE) != NULL) {
// subrefs are valid
// TODO: EXPECTED_DYNAMIC_VALUE in all cases?
for (size_t i = 0; i < subrefs.get_nof_refs(); ++i) {
type = type->get_type_refd_last();
FieldOrArrayRef* subref = subrefs.get_ref(i);
switch (subref->get_type()) {
case FieldOrArrayRef::FIELD_REF:
case FieldOrArrayRef::FUNCTION_REF:
if (type->get_typetype() == Common::Type::T_CLASS) {
ass = type->get_class_type_body()->
get_local_ass_byId(*subref->get_id());
type = ass->get_Type();
}
else {
type = type->get_comp_byName(*subref->get_id())->get_type();
}
break;
case FieldOrArrayRef::ARRAY_REF:
if (type->is_structured_type()) {
type = type->get_ofType();
}
break;
}
}
}
break; }
default:
break;
}
}
return ass;
}
const Identifier* Reference::get_modid()
{
if (!id) detect_modid();
return modid;
}
const Identifier* Reference::get_id()
{
if (!id) detect_modid();
return id;
}
ActualParList* Reference::get_parlist()
{
return parlist;
}
Type *Reference::chk_variable_ref()
{
Common::Assignment *t_ass = get_refd_assignment();
if (!t_ass) return 0;
switch (t_ass->get_asstype()) {
case Common::Assignment::A_PAR_VAL_IN:
t_ass->use_as_lvalue(*this);
// no break
case Common::Assignment::A_VAR:
case Common::Assignment::A_EXCEPTION:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT:
if (has_parameters()) {
error("Reference without actual parameter list was expected");
return NULL;
}
break;
default:
error("Reference to a variable or value parameter was "
"expected instead of %s", t_ass->get_description().c_str());
return NULL;
}
FieldOrArrayRefs *t_subrefs = get_subrefs();
Type *ret_val = t_ass->get_Type()->get_field_type(t_subrefs,
Type::EXPECTED_DYNAMIC_VALUE);
if (ret_val && t_subrefs && t_subrefs->refers_to_string_element()) {
error("Reference to a string element of type `%s' cannot be used in "
"this context", ret_val->get_typename().c_str());
}
return ret_val;
}
Type *Reference::chk_comptype_ref()
{
Common::Assignment *ass = get_refd_assignment();
if (ass) {
if (ass->get_asstype() == Common::Assignment::A_TYPE) {
Type *t = ass->get_Type()->get_type_refd_last();
switch (t->get_typetype()) {
case Type::T_ERROR:
// remain silent
break;
case Type::T_COMPONENT:
if (has_parameters()) {
error("Reference without actual parameter list was expected");
}
else {
return t;
}
break;
default:
error("Reference `%s' does not refer to a component type",
get_dispname().c_str());
}
} else {
error("Reference `%s' does not refer to a type",
get_dispname().c_str());
}
}
return 0;
}
bool Reference::chk_activate_argument()
{
Common::Assignment *t_ass = get_refd_assignment();
if (!has_parameters()) {
error("Reference with actual parameter list was expected in the argument");
return false;
}
if (!t_ass) return false;
if (t_ass->get_asstype() != Common::Assignment::A_ALTSTEP) {
error("Reference to an altstep was expected in the argument instead of "
"%s", t_ass->get_description().c_str());
return false;
}
my_scope->chk_runs_on_clause(t_ass, *this, "activate");
// the altstep reference cannot have sub-references
if (get_subrefs()) FATAL_ERROR("Reference::chk_activate_argument()");
FormalParList *fp_list = t_ass->get_FormalParList();
// the altstep must have formal parameter list
if (!fp_list) FATAL_ERROR("Reference::chk_activate_argument()");
return fp_list->chk_activate_argument(parlist,
t_ass->get_description().c_str());
}
void Reference::chk_ctor_defpar(bool default_ctor, bool in_base_call)
{
Common::Assignment* ass = get_refd_assignment_last();
bool is_var = false;
if (ass->get_my_scope()->is_class_scope()) {
switch (ass->get_asstype()) {
case Common::Assignment::A_VAR:
is_var = true;
if (default_ctor) {
error("Implicit constructor default parameter cannot contain reference to "
"variable class member `%s'",
ass->get_id().get_dispname().c_str());
break;
}
// no break
case Common::Assignment::A_CONST:
if (in_base_call) {
error("Super-constructor call cannot contain reference to %s class member `%s'",
is_var ? "variable" : "constant", ass->get_id().get_dispname().c_str());
}
else if (ass->get_Value() == NULL) {
error("Constructor default parameter cannot contain reference to "
"%s class member `%s', which has no initial value",
is_var ? "variable" : "constant", ass->get_id().get_dispname().c_str());
}
break;
case Common::Assignment::A_VAR_TEMPLATE:
is_var = true;
if (default_ctor) {
error("Implicit constructor default parameter cannot contain reference to "
"template variable class member `%s'",
ass->get_id().get_dispname().c_str());
break;
}
// no break
case Common::Assignment::A_TEMPLATE:
if (in_base_call) {
error("Super-constructor call cannot contain reference to template%s class member `%s'",
is_var ? " variable" : "", ass->get_id().get_dispname().c_str());
}
else if (ass->get_Template() == NULL) {
error("Constructor default parameter cannot contain reference to "
"template%s class member `%s', which has no initial value",
is_var ? " variable" : "", ass->get_id().get_dispname().c_str());
}
break;
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TIMER:
case Common::Assignment::A_PAR_PORT:
if (in_base_call) {
FormalPar* fp = dynamic_cast<FormalPar*>(ass);
if (fp == NULL) {
FATAL_ERROR("Reference::chk_ctor_defpar");
}
if (fp->has_defval()) {
fp->get_defval()->chk_ctor_defpar(default_ctor, in_base_call);
}
}
break;
default:
break;
}
}
}
void Reference::chk_class_member(ClassTypeBody* p_class)
{
Common::Assignment* ass = get_refd_assignment_last();
Scope* ass_parent_scope = refd_ass->get_my_scope()->get_parent_scope();
switch (ass->get_asstype()) {
case Common::Assignment::A_CONST:
case Common::Assignment::A_TEMPLATE:
case Common::Assignment::A_VAR:
case Common::Assignment::A_VAR_TEMPLATE:
case Common::Assignment::A_TIMER:
if (ass_parent_scope->is_class_scope() && ass_parent_scope->get_scope_class() == p_class) {
set_reftype(Ref_simple::REF_THIS);
}
break;
default:
break;
}
}
bool Reference::has_single_expr()
{
if (!Ref_base::has_single_expr()) {
return false;
}
if (reftype == REF_THIS && id == NULL) {
return false;
}
Common::Assignment* ass = get_refd_assignment();
if (subrefs.has_function_ref()) {
Common::Assignment* last_method = NULL;
Type* field_type = ass->get_Type()->get_field_type(&subrefs,
Common::Type::EXPECTED_DYNAMIC_VALUE, NULL, false, &last_method);
if (field_type != NULL &&
field_type->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) {
return false;
}
}
if (parlist != NULL) {
const FormalParList* fplist = (ass != NULL) ? ass->get_FormalParList() : NULL;
for (size_t i = 0; i < parlist->get_nof_pars(); i++) {
if (!parlist->get_par(i)->has_single_expr(
fplist != NULL ? fplist->get_fp_byIndex(i) : NULL)) {
return false;
}
}
if (fplist != NULL) {
size_t num_formal = fplist->get_nof_fps();
for (size_t i = 0; i < num_formal; ++i) {
const FormalPar *fp = fplist->get_fp_byIndex(i);
if (fp->get_eval_type() != NORMAL_EVAL) {
return false;
}
}
}
}
for (size_t i = 0; i < subrefs.get_nof_refs(); ++i) {
FieldOrArrayRef* subref = subrefs.get_ref(i);
if (subref->get_type() == FieldOrArrayRef::FUNCTION_REF) {
ActualParList* ap_list = subref->get_actual_par_list();
FormalParList* fp_list = subref->get_formal_par_list();
for (size_t j = 0; j < ap_list->get_nof_pars(); ++j) {
if (!ap_list->get_par(j)->has_single_expr(
fp_list != NULL ? fp_list->get_fp_byIndex(i) : NULL)) {
return false;
}
}
}
}
return true;
}
void Reference::ref_usage_found(Common::Assignment *ass)
{
if (!ass) FATAL_ERROR("Reference::ref_usage_found()");
switch (ass->get_asstype()) {
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_PORT:
case Common::Assignment::A_PAR_TIMER: {
FormalPar *fpar = dynamic_cast<FormalPar*>(ass);
if (fpar == NULL) {
FATAL_ERROR("Reference::ref_usage_found()");
}
fpar->set_usage_found();
break; }
case Common::Assignment::A_EXT_CONST: {
Def_ExtConst* def = dynamic_cast<Def_ExtConst*>(ass);
if (def == NULL) {
FATAL_ERROR("Reference::ref_usage_found()");
}
def->set_usage_found();
break; }
case Common::Assignment::A_EXCEPTION: {
Def_Exception* def = dynamic_cast<Def_Exception*>(ass);
if (def == NULL) {
FATAL_ERROR("Reference::ref_usage_found()");
}
def->set_usage_found();
break; }
default:
break;
}
}
void Reference::use_default_alternative(const Identifier& p_alt_name)
{
FieldOrArrayRef* alt_ref = new FieldOrArrayRef(new Identifier(p_alt_name));
alt_ref->set_my_scope(my_scope);
alt_ref->set_fullname(get_fullname() +
".<sub_reference" + Int2string(subrefs.get_nof_refs()) + ">");
subrefs.add(alt_ref);
}
void Reference::set_code_section(
GovernedSimple::code_section_t p_code_section)
{
Ref_base::set_code_section(p_code_section);
if (parlist != NULL) {
for (size_t i = 0; i < parlist->get_nof_pars(); i++) {
parlist->get_par(i)->set_code_section(p_code_section);
}
}
}
void Reference::generate_code(expression_struct_t *expr)
{
Common::Assignment *ass = get_refd_assignment();
if (!ass) FATAL_ERROR("Reference::generate_code()");
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'
return;
}
if (reftype == REF_VALUE) {
expr->expr = mputstr(expr->expr, ass->get_genname_from_scope(my_scope).c_str());
}
else {
if (parlist != NULL) {
expr->expr = mputprintf(expr->expr, "%s(",
ass->get_genname_from_scope(my_scope).c_str());
parlist->generate_code_alias(expr, ass->get_FormalParList(),
ass->get_RunsOnType(), false);
expr->expr = mputc(expr->expr, ')');
} else {
string const_prefix; // empty by default
string exception_postfix; // also empty by default
if (gen_const_prefix) {
if (ass->get_asstype() == Common::Assignment::A_CONST) {
const_prefix = "const_";
}
else if (ass->get_asstype() == Common::Assignment::A_TEMPLATE) {
const_prefix = "template_";
}
}
if (ass->get_asstype() == Common::Assignment::A_EXCEPTION) {
exception_postfix = "()";
}
expr->expr = mputstr(expr->expr,
LazyFuzzyParamData::in_lazy_or_fuzzy() ?
LazyFuzzyParamData::add_ref_genname(ass, my_scope).c_str() :
(get_class_defpar_prefix() + const_prefix + ass->get_genname_from_scope(my_scope) +
get_class_base_call_postfix() + exception_postfix).c_str());
}
}
if (subrefs.get_nof_refs() > 0) subrefs.generate_code(expr, ass, my_scope);
}
void Reference::generate_code_const_ref(expression_struct_t *expr)
{
FieldOrArrayRefs *t_subrefs = get_subrefs();
if (!t_subrefs || t_subrefs->get_nof_refs() == 0) {
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()) {
case Common::Assignment::A_MODULEPAR:
case Common::Assignment::A_VAR:
case Common::Assignment::A_EXCEPTION:
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT: {
is_template = false;
break; }
case Common::Assignment::A_MODULEPAR_TEMP:
case Common::Assignment::A_VAR_TEMPLATE:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TEMPL_INOUT: {
is_template = true;
break; }
case Common::Assignment::A_TEMPLATE:
if (ass->get_FormalParList() == NULL) {
// not a parameterized template
is_template = true;
break;
}
// else fall through
default:
generate_code(expr);
return;
}
Type *refd_gov = ass->get_Type();
if (refd_gov == NULL) {
generate_code(expr);
return;
}
// use a separate expression struct for this reference in case any of the
// subreferences need to convert the object further
expression_struct_t this_expr;
Code::init_expr(&this_expr);
if (is_template) {
this_expr.expr = mputprintf(this_expr.expr, "const_cast< const %s&>(",
refd_gov->get_genname_template(get_my_scope()).c_str() );
} else {
// don't convert to const object if the base reference is of class type
if (refd_gov->get_type_refd_last()->get_typetype() != Common::Type::T_CLASS) {
this_expr.expr = mputprintf(this_expr.expr, "const_cast< const %s&>(",
refd_gov->get_genname_value(get_my_scope()).c_str());
}
}
generate_class_specific_expr(&this_expr);
string exception_postfix;
if (ass->get_asstype() == Common::Assignment::A_EXCEPTION) {
exception_postfix = "()";
}
if (parlist != NULL) {
// reference without parameters to a template that has only default formal parameters.
// if @lazy: nothing to do, it's a C++ function call just like in case of Ref_pard::generate_code()
this_expr.expr = mputprintf(this_expr.expr, "%s(",
ass->get_genname_from_scope(my_scope).c_str());
parlist->generate_code_alias(&this_expr, ass->get_FormalParList(),
ass->get_RunsOnType(), false);
this_expr.expr = mputc(this_expr.expr, ')');
} else {
this_expr.expr = mputstr(this_expr.expr,
LazyFuzzyParamData::in_lazy_or_fuzzy() ?
LazyFuzzyParamData::add_ref_genname(ass, my_scope).c_str() :
(get_class_defpar_prefix() + ass->get_genname_from_scope(my_scope) +
get_class_base_call_postfix() + exception_postfix).c_str());
}
if (refd_gov->get_type_refd_last()->get_typetype() != Common::Type::T_CLASS) {
this_expr.expr = mputstr(this_expr.expr, ")");
}
t_subrefs->generate_code(&this_expr, ass, my_scope, true);
if (this_expr.preamble != NULL) {
expr->preamble = mputstr(expr->preamble, this_expr.preamble);
}
Common::Assignment* last_method = NULL;
Type* field_type = refd_gov->get_field_type(t_subrefs,
Common::Type::EXPECTED_DYNAMIC_VALUE, NULL, false, &last_method);
if (field_type != NULL && t_subrefs->has_function_ref() &&
field_type->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) {
// If the reference contains a class method call, then the end result is a temporary
// object in C++. If it's of a class type, then the C++ compiler can't automatically
// convert the temporary object to a constant reference of a base class (in case the
// reference is used as an 'in' actual parameter).
// For safety, the end result is copied in these cases.
string tmp_str = my_scope->get_scope_mod_gen()->get_temporary_id();
expr->preamble = mputprintf(expr->preamble,
"%s %s(%s);\n",
field_type->get_genname_value(my_scope).c_str(), tmp_str.c_str(), this_expr.expr);
if (this_expr.postamble != NULL) {
expr->preamble = mputstr(expr->preamble, this_expr.postamble);
}
expr->expr = mputstr(expr->expr, tmp_str.c_str());
}
else {
expr->expr = mputstr(expr->expr, this_expr.expr);
if (this_expr.postamble != NULL) {
expr->postamble = mputstr(expr->postamble, this_expr.postamble);
}
}
Code::free_expr(&this_expr);
}
void Reference::generate_code_portref(expression_struct_t *expr,
Scope *p_scope)
{
Common::Assignment *ass = get_refd_assignment();
if (!ass) FATAL_ERROR("Reference::generate_code_portref()");
expr->expr = mputstr(expr->expr,
ass->get_genname_from_scope(p_scope).c_str());
if (subrefs.get_nof_refs() > 0) subrefs.generate_code(expr, ass, my_scope);
}
//FIXME quick hack
void Reference::generate_code_ispresentboundchosen(expression_struct_t *expr,
bool is_template, const Value::operationtype_t optype, const char* field)
{
Common::Assignment *ass = get_refd_assignment();
const string& ass_id = ass->get_genname_from_scope(my_scope);
const char *ass_id_str = ass_id.c_str();
expression_struct_t t;
Code::init_expr(&t);
// Generate the default parameters if needed
if (parlist) {
parlist->generate_code_alias(&t, ass->get_FormalParList(),
ass->get_RunsOnType(), false);
}
string ass_id2 = ass_id;
if (t.preamble != NULL) {
expr->preamble = mputstr(expr->preamble, t.preamble);
}
if (t.expr != NULL) {
ass_id2 = ass_id2 + "(" + t.expr + ")";
ass_id_str = ass_id2.c_str();
}
Code::free_expr(&t);
if (subrefs.get_nof_refs() > 0) {
const string& tmp_generalid = my_scope->get_scope_mod_gen()
->get_temporary_id();
const char *tmp_generalid_str = tmp_generalid.c_str();
expression_struct isbound_expr;
Code::init_expr(&isbound_expr);
isbound_expr.preamble = mputprintf(isbound_expr.preamble,
"boolean %s = %s.is_bound();\n", tmp_generalid_str,
ass_id_str);
namedbool p_optype;
if (optype == Value::OPTYPE_ISBOUND) {
p_optype = ISBOUND;
} else if (optype == Value::OPTYPE_ISPRESENT) {
p_optype = ISPRESENT;
} else if (optype == Value::OPTYPE_ISCHOSEN_T || optype == Value::OPTYPE_ISCHOSEN_V) {
p_optype = ISCHOSEN;
} else if (optype == Value::OPTYPE_ISVALUE) {
p_optype = ISVALUE;
} else {
FATAL_ERROR("AST_ttcn3.cc::generate_code_ispresentboundchosen()");
}
ass->get_Type()->generate_code_ispresentboundchosen(&isbound_expr, &subrefs, my_scope->get_scope_mod_gen(),
tmp_generalid, ass_id2, is_template, p_optype, field);
expr->preamble = mputstr(expr->preamble, isbound_expr.preamble);
expr->preamble = mputstr(expr->preamble, isbound_expr.expr);
Code::free_expr(&isbound_expr);
expr->expr = mputprintf(expr->expr, "%s", tmp_generalid_str);
} else {
expr->expr = mputprintf(expr->expr, "%s.", ass_id_str);
switch (optype) {
case Value::OPTYPE_ISBOUND:
expr->expr = mputstr(expr->expr, "is_bound()");
break;
case Value::OPTYPE_ISPRESENT:
expr->expr = mputprintf(expr->expr, "is_present(%s)",
is_template && omit_in_value_list ? "TRUE" : "");
break;
case Value::OPTYPE_ISCHOSEN_T:
case Value::OPTYPE_ISCHOSEN_V:
expr->expr = mputprintf(expr->expr, "ischosen(%s)", field);
break;
case Value::OPTYPE_ISVALUE:
expr->expr = mputprintf(expr->expr, "is_value()");
break;
default:
FATAL_ERROR("AST_ttcn3.cc::generate_code_ispresentboundchosen()");
}
}
}
void Reference::generate_code_cached(expression_struct_t *expr)
{
if (expr_cache) {
expr->expr = mputstr(expr->expr, expr_cache);
}
else {
generate_code(expr);
expr_cache = mputstr(expr_cache, expr->expr);
}
}
void Reference::detect_modid()
{
// do nothing if detection is already performed
if (id || reftype == REF_THIS || reftype == REF_VALUE) return;
// the first element of subrefs must be an <id>
const Identifier *first_id = subrefs.get_ref(0)->get_id(), *second_id = 0;
const ParsedActualParameters* second_params = 0;
if (subrefs.get_nof_refs() > 1) {
FieldOrArrayRef *second_ref = subrefs.get_ref(1);
if (second_ref->get_type() != FieldOrArrayRef::ARRAY_REF) {
// the reference begins with <id>.<id> or <id>.<id>(<params>)
// (most complicated case); there are 3 possible situations:
// 1. first_id points to a local definition (this has the priority)
// modid: 0, id: first_id
// 2. first_id points to an imported module (trivial case)
// modid: first_id, id: second_id
// 3. none of the above (first_id might be an imported symbol)
// modid: 0, id: first_id
// Note: Rule 1 has the priority because it can be overridden using
// the notation <id>.objid { ... }.<id> or <id>.objid { ... }.<id>(<params>)
// (modid and id are set in the constructor), but there is no work-around
// in the reverse way.
if (!my_scope->has_ass_withId(*first_id)
&& my_scope->is_valid_moduleid(*first_id)) {
// rule 1 is not fulfilled, but rule 2 is fulfilled
second_id = second_ref->get_id();
if (second_ref->get_type() == FieldOrArrayRef::FUNCTION_REF) {
// <id>.<id>(<params>) case
second_params = second_ref->get_parsed_pars();
}
}
} // else: the reference begins with <id>[<arrayref>] -> there is no modid
} // else: the reference consists of a single <id> -> there is no modid
if (second_id) {
modid = first_id->clone();
id = second_id->clone();
if (second_params != 0) {
params = second_params->clone();
params->set_fullname(second_params->get_fullname());
params->set_my_scope(second_params->get_my_scope());
}
subrefs.remove_refs(2);
} else {
modid = 0;
id = first_id->clone();
subrefs.remove_refs(1);
}
}
void Reference::generate_class_specific_expr(expression_struct_t *expr)
{
if (reftype != REF_THIS && reftype != REF_SUPER) {
return;
}
Common::Assignment *ass = get_refd_assignment();
if (ass == NULL) {
FATAL_ERROR("Reference::generate_class_specific_expr()");
}
if (reftype == REF_THIS) {
if (id != NULL && !gen_class_defpar_prefix) {
expr->expr = mputstr(expr->expr, "this->");
}
else if (id == NULL) { // no 'id' means it's just a 'this' reference
string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
expr->preamble = mputprintf(expr->preamble, "%s %s(this);\n",
ass->get_Type()->get_genname_value(my_scope).c_str(), tmp_id.c_str());
expr->expr = mputprintf(expr->expr, "%s", tmp_id.c_str());
}
}
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::",
base_type->get_class_type_body()->is_built_in() ? "OBJECT" :
base_type->get_genname_own(my_scope).c_str());
}
}
string Reference::get_class_defpar_prefix()
{
if (gen_class_defpar_prefix) {
Common::Assignment *ass = get_refd_assignment();
if (ass == NULL) {
FATAL_ERROR("Reference::get_class_defpar_prefix()");
}
if (ass->get_my_scope()->is_class_scope()) {
return string("p_class->");
}
}
return string();
}
string Reference::get_class_base_call_postfix()
{
if (gen_class_base_call_postfix) {
Common::Assignment *ass = get_refd_assignment();
if (ass == NULL) {
FATAL_ERROR("Reference::get_class_base_call_postfix()");
}
switch (ass->get_asstype()) {
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TIMER: {
FormalPar* formal_par = dynamic_cast<FormalPar*>(ass);
if (formal_par == NULL) {
FATAL_ERROR("Reference::get_class_base_call_postfix");
}
Definition* fp_def = formal_par->get_my_parlist()->get_my_def();
if (fp_def->get_asstype() == Common::Assignment::A_CONSTRUCTOR && formal_par->has_defval()) {
return string("_defpar(this)");
}
break; }
default:
break;
}
}
return string();
}
// =================================
// ===== NameBridgingScope
// =================================
string NameBridgingScope::get_scopeMacro_name() const
{
if (scopeMacro_name.empty()) FATAL_ERROR("NameBridgingScope::get_scopeMacro_name()");
return scopeMacro_name;
}
NameBridgingScope *NameBridgingScope::clone() const
{
FATAL_ERROR("NameBridgingScope::clone");
}
Common::Assignment* NameBridgingScope::get_ass_bySRef(Ref_simple *p_ref)
{
return get_parent_scope()->get_ass_bySRef(p_ref);
}
bool NameBridgingScope::is_class_scope() const
{
if (parent_scope == NULL) {
FATAL_ERROR("NameBridgingScope::is_class_scope()");
}
return parent_scope->is_class_scope();
}
// =================================
// ===== RunsOnScope
// =================================
RunsOnScope::RunsOnScope(Type *p_comptype)
: Scope(), component_type(p_comptype)
{
if (!p_comptype || p_comptype->get_typetype() != Type::T_COMPONENT)
FATAL_ERROR("RunsOnScope::RunsOnScope()");
component_type->set_ownertype(Type::OT_RUNSON_SCOPE, this);
component_defs = p_comptype->get_CompBody();
set_scope_name("runs on `" + p_comptype->get_fullname() + "'");
}
RunsOnScope *RunsOnScope::clone() const
{
FATAL_ERROR("RunsOnScope::clone()");
}
void RunsOnScope::chk_uniq()
{
// do not perform this check if the component type is defined in the same
// module as the 'runs on' clause
if (parent_scope->get_scope_mod() == component_defs->get_scope_mod())
return;
size_t nof_defs = component_defs->get_nof_asss();
for (size_t i = 0; i < nof_defs; i++) {
Common::Assignment *comp_def = component_defs->get_ass_byIndex(i);
const Identifier& id = comp_def->get_id();
if (parent_scope->has_ass_withId(id)) {
comp_def->warning("Imported component element definition `%s' hides a "
"definition at module scope", comp_def->get_fullname().c_str());
Reference ref(0, id.clone());
Common::Assignment *hidden_ass = parent_scope->get_ass_bySRef(&ref);
hidden_ass->warning("Hidden definition `%s' is here",
hidden_ass->get_fullname().c_str());
}
}
}
RunsOnScope *RunsOnScope::get_scope_runs_on()
{
return this;
}
Common::Assignment *RunsOnScope::get_ass_bySRef(Ref_simple *p_ref)
{
if (!p_ref) FATAL_ERROR("Ttcn::RunsOnScope::get_ass_bySRef()");
if (p_ref->get_modid() || p_ref->get_reftype() != Ref_simple::REF_BASIC) {
return parent_scope->get_ass_bySRef(p_ref);
}
else {
const Identifier& id = *p_ref->get_id();
if (component_defs->has_local_ass_withId(id)) {
Common::Assignment* ass = component_defs->get_local_ass_byId(id);
if (!ass) FATAL_ERROR("Ttcn::RunsOnScope::get_ass_bySRef()");
if (component_defs->is_own_assignment(ass)) return ass;
else if (ass->get_visibility() == PUBLIC) {
return ass;
}
p_ref->error("The member definition `%s' in component type `%s'"
" is not visible in this scope", id.get_dispname().c_str(),
component_defs->get_id()->get_dispname().c_str());
return 0;
} else return parent_scope->get_ass_bySRef(p_ref);
}
}
bool RunsOnScope::has_ass_withId(const Identifier& p_id)
{
return component_defs->has_ass_withId(p_id)
|| parent_scope->has_ass_withId(p_id);
}
// =================================
// ===== PortScope
// =================================
PortScope::PortScope(Type *p_porttype)
: Scope(), port_type(p_porttype)
{
if (!p_porttype || p_porttype->get_typetype() != Type::T_PORT)
FATAL_ERROR("PortScope::PortScope()");
port_type->set_ownertype(Type::OT_PORT_SCOPE, this);
PortTypeBody* ptb = p_porttype->get_PortBody();
if (!ptb)
FATAL_ERROR("PortScope::PortScope()");
vardefs = ptb->get_vardefs();
set_scope_name("port `" + p_porttype->get_fullname() + "'");
}
PortScope *PortScope::clone() const
{
FATAL_ERROR("PortScope::clone()");
}
void PortScope::chk_uniq()
{
if (!vardefs) return;
if (vardefs->get_nof_asss() == 0) return;
// do not perform this check if the port type is defined in the same
// module as the 'port' clause
if (parent_scope->get_scope_mod() == vardefs->get_scope_mod())
return;
size_t nof_defs = vardefs->get_nof_asss();
for (size_t i = 0; i < nof_defs; i++) {
Common::Assignment *vardef = vardefs->get_ass_byIndex(i);
const Identifier& id = vardef->get_id();
if (parent_scope->has_ass_withId(id)) {
vardef->warning("Imported port element definition `%s' hides a "
"definition at module scope", vardef->get_fullname().c_str());
Reference ref(0, id.clone());
Common::Assignment *hidden_ass = parent_scope->get_ass_bySRef(&ref);
hidden_ass->warning("Hidden definition `%s' is here",
hidden_ass->get_fullname().c_str());
}
}
}
PortScope *PortScope::get_scope_port()
{
return this;
}
Common::Assignment *PortScope::get_ass_bySRef(Ref_simple *p_ref)
{
if (!p_ref) FATAL_ERROR("Ttcn::PortScope::get_ass_bySRef()");
if (p_ref->get_modid()) return parent_scope->get_ass_bySRef(p_ref);
else {
const Identifier& id = *p_ref->get_id();
if (vardefs->has_local_ass_withId(id)) {
Common::Assignment* ass = vardefs->get_local_ass_byId(id);
if (!ass) FATAL_ERROR("Ttcn::PortScope::get_ass_bySRef()");
return ass;
} else return parent_scope->get_ass_bySRef(p_ref);
}
}
bool PortScope::has_ass_withId(const Identifier& p_id)
{
return vardefs->has_ass_withId(p_id)
|| parent_scope->has_ass_withId(p_id);
}
// =================================
// ===== FriendMod
// =================================
FriendMod::FriendMod(Identifier *p_modid)
: Node(), modid(p_modid), w_attrib_path(0), parentgroup(0), checked(false)
{
if (!p_modid) FATAL_ERROR("NULL parameter: Ttcn::FriendMod::FriendMod()");
set_fullname("<friends>."+modid->get_dispname());
}
FriendMod::~FriendMod()
{
delete modid;
delete w_attrib_path;
}
FriendMod *FriendMod::clone() const
{
FATAL_ERROR("Ttcn::FriendMod::clone()");
}
void FriendMod::set_fullname(const string& p_fullname)
{
if(w_attrib_path) w_attrib_path->set_fullname(p_fullname + ".<attribpath>");
}
void FriendMod::chk()
{
if (checked) return;
Error_Context cntxt(this, "In friend module declaration");
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
checked = true;
}
void FriendMod::set_with_attr(MultiWithAttrib* p_attrib)
{
if (w_attrib_path) FATAL_ERROR("FriendMod::set_with_attr()");
w_attrib_path = new WithAttribPath();
if (p_attrib && p_attrib->get_nof_elements() > 0) {
w_attrib_path->set_with_attr(p_attrib);
}
}
WithAttribPath* FriendMod::get_attrib_path()
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
return w_attrib_path;
}
void FriendMod::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_parent(p_path);
}
void FriendMod::set_parent_group(Group* p_group)
{
if(parentgroup) FATAL_ERROR("FriendMod::set_parent_group");
parentgroup = p_group;
}
// =================================
// ===== ImpMod
// =================================
ImpMod::ImpMod(Identifier *p_modid)
: Node(), mod(0), my_mod(0), imptype(I_UNDEF), modid(p_modid),
language_spec(0), is_recursive(false),
w_attrib_path(0), parentgroup(0), visibility(PRIVATE)
{
if (!p_modid) FATAL_ERROR("NULL parameter: Ttcn::ImpMod::ImpMod()");
set_fullname("<imports>." + modid->get_dispname());
}
ImpMod::~ImpMod()
{
delete modid;
delete language_spec;
delete w_attrib_path;
}
ImpMod *ImpMod::clone() const
{
FATAL_ERROR("No clone for you!");
}
void ImpMod::set_fullname(const string& p_fullname)
{
if(w_attrib_path) w_attrib_path->set_fullname(p_fullname + ".<attribpath>");
}
void ImpMod::chk()
{
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
void ImpMod::chk_imp(ReferenceChain& refch, vector<Common::Module>& moduleStack)
{
if (imptype == I_DEPENDENCY) {
// these are added during semantic analysis, including their module pointer
return;
}
Error_Context cntxt(this, "In import definition");
if (!modules->has_mod_withId(*modid)) {
error("There is no module with identifier `%s'",
modid->get_dispname().c_str());
return;
}
Common::Module *m = modules->get_mod_byId(*modid);
if (m == NULL)
return;
set_mod(m);
if (m == my_mod) {
error("A module cannot import from itself");
return;
}
chk();
moduleStack.add(m);
refch.mark_state();
if (refch.exists(my_mod->get_fullname())) {
if(my_mod->get_moduletype()!=Common::Module::MOD_ASN){ // Do not warning for circular import in ASN.1 module. It is legal
my_mod->warning("Circular import chain is not recommended: %s",
refch.get_dispstr(my_mod->get_fullname()).c_str());
}
refch.prev_state();
return;
} else {
refch.add(my_mod->get_fullname());
if (ImpMod::I_IMPORTIMPORT == imptype){
Ttcn::Module* ttcnmodule =static_cast<Ttcn::Module*>(m);
const Imports& imp = ttcnmodule->get_imports();
for (size_t t = 0; t < imp.impmods_v.size(); t++) {
const ImpMod *im = imp.impmods_v[t];
refch.mark_state();
if (PRIVATE != im->get_visibility()) {
if (refch.exists(m->get_fullname())) {
if(m->get_moduletype()!=Common::Module::MOD_ASN){ // Do not warn for circular import in ASN.1 module. It is legal
m->warning("Circular import chain is not recommended: %s",
refch.get_dispstr(m->get_fullname()).c_str());
}
refch.prev_state();
continue;
} else {
const Identifier& im_id = im->get_modid();
Common::Module *cm = modules->get_mod_byId(im_id); // never NULL
refch.add(m->get_fullname());
cm->chk_imp(refch, moduleStack);
}
}
refch.prev_state();
}
} else {
//refch.mark_state();
m->chk_imp(refch, moduleStack);
//refch.prev_state();
}
}
refch.prev_state();
size_t state=moduleStack.size();
moduleStack.replace(state, moduleStack.size() - state);
}
bool ImpMod::has_imported_def(const Identifier& p_source_modid,
const Identifier& p_id, const Location *loc) const
{
if (!mod) return false;
else {
switch (imptype) {
case I_ALL:
case I_IMPORTSPEC:
{
Common::Assignment* return_assignment = mod->importAssignment(p_source_modid, p_id);
if (return_assignment != NULL) {
return true;
} else {
return false;
}
break;
}
case I_IMPORTIMPORT:
{
Ttcn::Module *tm = static_cast<Ttcn::Module*>(mod); // B
const Imports & imps = tm->get_imports();
vector<ImpMod> tempusedImpMods;
for (size_t i = 0, num = imps.impmods_v.size(); i < num; ++i) {
ReferenceChain* referencechain = new ReferenceChain(this, "NEW IMPORT REFERNCECHAIN");
Common::Assignment* return_assignment = imps.impmods_v[i]->
get_imported_def(p_source_modid, p_id, loc, referencechain, tempusedImpMods); // C
referencechain->reset();
delete referencechain;
if (return_assignment != NULL) {
return true;
} else {
return false;
}
}
//satisfy destructor
tempusedImpMods.clear();
break;
}
case I_DEPENDENCY:
return false;
default:
FATAL_ERROR("ImpMod::get_imported_def");
}
}
return false;
}
Common::Assignment *ImpMod::get_imported_def(
const Identifier& p_source_modid, const Identifier& p_id,
const Location *loc, ReferenceChain* refch,
vector<ImpMod>& usedImpMods) const
{
if (!mod) return 0;
else {
Common::Assignment* result = NULL;
switch (imptype) {
case I_ALL:
case I_IMPORTSPEC:
result = mod->importAssignment(p_source_modid, p_id);
if (result != NULL) {
usedImpMods.add(const_cast<Ttcn::ImpMod*>(this));
}
return result;
break;
case I_IMPORTIMPORT:
{
Ttcn::Module *tm = static_cast<Ttcn::Module*>(mod);
const Imports & imps = tm->get_imports();
Common::Assignment* t_ass = NULL;
for (size_t i = 0, num = imps.impmods_v.size(); i < num; ++i) {
vector<ImpMod> tempusedImpMods;
refch->mark_state();
if (imps.impmods_v[i]->get_visibility() == PUBLIC) {
t_ass = imps.impmods_v[i]->get_imported_def(p_source_modid, p_id, loc, refch, tempusedImpMods );
}
else if (imps.impmods_v[i]->get_visibility() == FRIEND) {
t_ass = imps.impmods_v[i]->get_imported_def(p_source_modid, p_id, loc, refch, tempusedImpMods );
if (t_ass != NULL){
tempusedImpMods.add(imps.impmods_v[i]);
}
}
refch->prev_state();
if (t_ass != NULL) {
bool visible = true;
for (size_t j = 0; j < tempusedImpMods.size(); j++) {
ImpMod* impmod_l = tempusedImpMods[j];
//check whether the module is TTCN
if (impmod_l->get_mod()->get_moduletype() == Common::Module::MOD_TTCN) {
// cast to ttcn module
Ttcn::Module *ttcn_m = static_cast<Ttcn::Module*>(impmod_l->get_mod());
if (!ttcn_m->is_visible(mod->get_modid(), impmod_l->get_visibility())) {
visible= false;
}
}
}
if (visible) {
for (size_t t = 0; i< tempusedImpMods.size(); i++) {
usedImpMods.add(tempusedImpMods[t]);
}
if (!result) {
result = t_ass;
} else if(result != t_ass) {
if (loc == NULL) {
result = NULL;
} else {
loc->error(
"It is not possible to resolve the reference unambiguously"
", as it can be resolved to `%s' and to `%s'",
result->get_fullname().c_str(), t_ass->get_fullname().c_str());
}
}
}
t_ass = NULL;
}
tempusedImpMods.clear();
}
if (result != NULL) {
usedImpMods.add(const_cast<Ttcn::ImpMod*>(this));
}
return result;
break;
}
case I_DEPENDENCY:
return NULL;
default:
FATAL_ERROR("ImpMod::get_imported_def");
}
}
}
void ImpMod::set_language_spec(const char *p_language_spec)
{
if (language_spec) FATAL_ERROR("ImpMod::set_language_spec()");
if (p_language_spec) language_spec = new string(p_language_spec);
}
void ImpMod::generate_code(output_struct *target)
{
const char *module_name = modid->get_name().c_str();
target->header.includes = mputprintf(target->header.includes,
"#include \"%s.hh\"\n",
duplicate_underscores ? module_name : modid->get_ttcnname().c_str());
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s%s.pre_init_module();\n", module_name,
"::module_object");
if (mod->get_moduletype() == Common::Module::MOD_TTCN) {
target->functions.post_init = mputprintf(target->functions.post_init,
"%s%s.post_init_module();\n", module_name,
"::module_object");
}
}
void ImpMod::dump(unsigned level) const
{
DEBUG(level, "Import from module %s", modid->get_dispname().c_str());
if (w_attrib_path) {
MultiWithAttrib *attrib = w_attrib_path->get_with_attr();
if (attrib) {
DEBUG(level + 1, "Attributes:");
attrib->dump(level + 2);
}
}
}
void ImpMod::set_with_attr(MultiWithAttrib* p_attrib)
{
if (w_attrib_path) FATAL_ERROR("ImpMod::set_with_attr()");
w_attrib_path = new WithAttribPath();
if (p_attrib && p_attrib->get_nof_elements() > 0) {
w_attrib_path->set_with_attr(p_attrib);
}
}
WithAttribPath* ImpMod::get_attrib_path()
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
return w_attrib_path;
}
void ImpMod::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_parent(p_path);
}
void ImpMod::set_parent_group(Group* p_group)
{
if(parentgroup) FATAL_ERROR("ImpMod::set_parent_group");
parentgroup = p_group;
}
// =================================
// ===== Imports
// =================================
Imports::~Imports()
{
for (size_t i = 0; i< impmods_v.size(); i++)
delete impmods_v[i];
impmods_v.clear();
}
Imports *Imports::clone() const
{
FATAL_ERROR("Ttcn::Imports::clone()");
}
void Imports::add_impmod(ImpMod *p_impmod)
{
if (!p_impmod) FATAL_ERROR("Ttcn::Imports::add_impmod()");
if (p_impmod->get_imptype() == ImpMod::I_DEPENDENCY) {
// this is just an extra dependency, not an actual 'import' in TTCN-3 code,
// only insert it if it's needed
for (size_t i = 0; i < impmods_v.size(); ++i) {
if (p_impmod->get_mod() == impmods_v[i]->get_mod()) {
delete p_impmod;
return;
}
}
}
impmods_v.add(p_impmod);
p_impmod->set_my_mod(my_mod);
}
void Imports::set_my_mod(Module *p_mod)
{
my_mod = p_mod;
for(size_t i = 0; i < impmods_v.size(); i++)
impmods_v[i]->set_my_mod(my_mod);
}
bool Imports::has_impmod_withId(const Identifier& p_id) const
{
for (size_t i = 0, size = impmods_v.size(); i < size; ++i) {
const ImpMod* im = impmods_v[i];
const Identifier& im_id = im->get_modid();
const string& im_name = im_id.get_name();
if (p_id.get_name() == im_name) {
// The identifier represents a module imported in the current module
return true;
}
}
return false;
}
void Imports::chk_imp(ReferenceChain& refch, vector<Common::Module>& moduleStack)
{
if (impmods_v.size() <= 0) return;
if (!my_mod) FATAL_ERROR("Ttcn::Imports::chk_imp()");
checked = true;
//Do some checks
for(size_t n = 0; n < impmods_v.size(); n++)
{
impmods_v[n]->chk();
}
//TODO this whole thing should be moved into impmod::chk
Identifier address_id(Identifier::ID_TTCN, string("address"));
for (size_t n = 0; n < impmods_v.size(); n++) {
ImpMod *im = impmods_v[n];
im->chk_imp(refch, moduleStack);
const Identifier& im_id = im->get_modid();
Common::Module *m = modules->get_mod_byId(im_id);
if (m == NULL)
continue;
if (m->get_gen_code()) my_mod->set_gen_code();
} // next assignment
}
bool Imports::has_imported_def(const Identifier& p_id, const Location *loc) const
{
for (size_t n = 0; n < impmods_v.size(); n++) {
ImpMod *im = impmods_v[n];
bool return_bool = im->has_imported_def(my_mod->get_modid(), p_id, loc);
if (return_bool) return true;
}
return false;
}
Common::Assignment *Imports::get_imported_def(
const Identifier& p_source_modid, const Identifier& p_id,
const Location *loc, ReferenceChain* refch)
{
vector<ImpMod> tempusedImpMods;
Common::Assignment* result = NULL;
for (size_t n = 0; n < impmods_v.size(); n++) {
ImpMod *im = impmods_v[n];
refch->mark_state();
Common::Assignment* ass = im->get_imported_def(
p_source_modid, p_id, loc, refch, tempusedImpMods);
tempusedImpMods.clear();
refch->prev_state();
if(ass) {
if (!result) {
result = ass;
} else if(result != ass && loc) {
if(loc == NULL) {
result = NULL;
} else {
loc->error(
"It is not possible to resolve the reference unambiguously"
", as it can be resolved to `%s' and to `%s'",
result->get_fullname().c_str(), ass->get_fullname().c_str());
}
}
}
}
return result;
}
void Imports::get_imported_mods(Module::module_set_t& p_imported_mods) const
{
for (size_t i = 0; i < impmods_v.size(); i++) {
ImpMod *im = impmods_v[i];
Common::Module *m = im->get_mod();
if (!m) continue;
if (!p_imported_mods.has_key(m)) {
p_imported_mods.add(m, 0);
m->get_visible_mods(p_imported_mods);
}
}
}
void Imports::generate_code(output_struct *target)
{
target->header.includes = mputstr(target->header.includes,
"#include <TTCN3.hh>\n");
for (size_t i = 0; i < impmods_v.size(); i++) {
ImpMod *im = impmods_v[i];
Common::Module *m = im->get_mod();
// inclusion of m's header file can be eliminated if we find another
// imported module that imports m
bool covered = false;
for (size_t j = 0; j < impmods_v.size(); j++) {
// skip over the same import definition
if (j == i) continue;
ImpMod *im2 = impmods_v[j];
Common::Module *m2 = im2->get_mod();
// a module that is equivalent to the current module due to
// circular imports cannot be used to cover anything
if (m2->is_visible(my_mod)) continue;
if (m2->is_visible(m) && !m->is_visible(m2)) {
// m2 covers m (i.e. m is visible from m2)
// and they are not in the same import loop
covered = true;
break;
}
}
// do not generate the #include if a covering module is found
if (!covered) im->generate_code(target);
}
}
void Imports::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
void Imports::dump(unsigned level) const
{
DEBUG(level, "Imports (%lu pcs.)", static_cast<unsigned long>( impmods_v.size() ));
for (size_t i = 0; i < impmods_v.size(); i++)
impmods_v[i]->dump(level + 1);
}
// =================================
// ===== Definitions
// =================================
Definitions::~Definitions()
{
for(size_t i = 0; i < ass_v.size(); i++) delete ass_v[i];
ass_v.clear();
ass_m.clear();
}
Definitions *Definitions::clone() const
{
FATAL_ERROR("Definitions::clone");
}
void Definitions::add_ass(Definition *p_ass)
{
// it is too late to add a new one after it has been checked.
if (checked || !p_ass) FATAL_ERROR("Ttcn::OtherDefinitions::add_ass()");
ass_v.add(p_ass);
p_ass->set_my_scope(this);
}
void Definitions::set_fullname(const string& p_fullname)
{
Common::Assignments::set_fullname(p_fullname);
for(size_t i = 0; i < ass_v.size(); i++) {
Definition *ass = ass_v[i];
ass->set_fullname(p_fullname + "." + ass->get_id().get_dispname());
}
}
bool Definitions::has_local_ass_withId(const Identifier& p_id)
{
if (!checked) chk_uniq();
return ass_m.has_key(p_id.get_name());
}
Common::Assignment* Definitions::get_local_ass_byId(const Identifier& p_id)
{
if (!checked) chk_uniq();
return ass_m[p_id.get_name()];
}
size_t Definitions::get_nof_asss()
{
if (!checked) chk_uniq();
return ass_m.size();
}
size_t Definitions::get_nof_raw_asss()
{
return ass_v.size();
}
Common::Assignment* Definitions::get_ass_byIndex(size_t p_i, bool p_reordered)
{
if (!checked) chk_uniq();
return p_reordered ? ass_m.get_nth_elem(p_i) : ass_v[p_i];
}
Ttcn::Definition* Definitions::get_raw_ass_byIndex(size_t p_i) {
return ass_v[p_i];
}
bool Definitions::is_class_scope() const
{
if (parent_scope == NULL) {
FATAL_ERROR("Definitions::is_class_scope()");
}
return parent_scope->is_class_scope();
}
void Definitions::chk_uniq()
{
if (checked) return;
ass_m.clear();
for (size_t i = 0; i < ass_v.size(); i++) {
Definition *ass = ass_v[i];
const Identifier& id = ass->get_id();
const string& name = id.get_name();
if (ass_m.has_key(name)) {
const char *dispname_str = id.get_dispname().c_str();
ass->error("Duplicate definition with name `%s'", dispname_str);
ass_m[name]->note("Previous definition of `%s' is here", dispname_str);
} else {
ass_m.add(name, ass);
if (parent_scope->is_valid_moduleid(id)) {
ass->warning("Definition with name `%s' hides a module identifier",
id.get_dispname().c_str());
}
}
}
checked = true;
}
void Definitions::chk()
{
for (size_t i = 0; i < ass_v.size(); i++) ass_v[i]->chk();
}
void Definitions::chk_for()
{
if (checked) return;
checked = true;
ass_m.clear();
// all checks are done in one iteration because
// forward referencing is not allowed between the definitions
for (size_t i = 0; i < ass_v.size(); i++) {
Definition *def = ass_v[i];
const Identifier& id = def->get_id();
const string& name = id.get_name();
if (ass_m.has_key(name)) {
const char *dispname_str = id.get_dispname().c_str();
def->error("Duplicate definition with name `%s'", dispname_str);
ass_m[name]->note("Previous definition of `%s' is here", dispname_str);
} else {
ass_m.add(name, def);
if (parent_scope) {
if (parent_scope->has_ass_withId(id)) {
if (parent_scope->get_scope_class() == NULL) {
const char *dispname_str = id.get_dispname().c_str();
def->error("Definition with identifier `%s' is not unique in the "
"scope hierarchy", dispname_str);
Reference ref(0, id.clone());
Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref);
if (!ass) FATAL_ERROR("OtherDefinitions::chk_for()");
ass->note("Previous definition with identifier `%s' in higher "
"scope unit is here", dispname_str);
}
} else if (parent_scope->is_valid_moduleid(id)) {
def->warning("Definition with name `%s' hides a module identifier",
id.get_dispname().c_str());
}
}
}
def->chk();
}
}
void Definitions::set_genname(const string& prefix)
{
for (size_t i = 0; i < ass_v.size(); i++) {
Definition *def = ass_v[i];
def->set_genname(prefix + def->get_id().get_name());
}
}
void Definitions::generate_code(output_struct* target)
{
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();
target->header.class_defs = mputprintf(target->header.class_defs,
"\n%s:\n", vis == PUBLIC ? "public" : (vis == PRIVATE ? "private" :
"protected"));
}
ass_v[i]->generate_code(target);
}
}
void Definitions::generate_code(CodeGenHelper& cgh) {
// FIXME: implement
for(size_t i = 0; i < ass_v.size(); i++) {
ass_v[i]->generate_code(cgh);
CodeGenHelper::update_intervals(cgh.get_current_outputstruct());
}
}
char* Definitions::generate_code_str(char *str)
{
for(size_t i=0; i<ass_v.size(); i++) {
str = ass_v[i]->update_location_object(str);
str=ass_v[i]->generate_code_str(str);
}
return str;
}
void Definitions::ilt_generate_code(ILT *ilt)
{
for(size_t i=0; i<ass_v.size(); i++)
ass_v[i]->ilt_generate_code(ilt);
}
void Definitions::dump(unsigned level) const
{
DEBUG(level, "Definitions: (%lu pcs.)", static_cast<unsigned long>( ass_v.size() ));
for(size_t i = 0; i < ass_v.size(); i++) ass_v[i]->dump(level + 1);
}
// =================================
// ===== Group
// =================================
Group::Group(Identifier *p_id)
: Node(), Location(), parent_group(0), w_attrib_path(0), id(p_id),
checked(false)
{
if (!p_id) FATAL_ERROR("Group::Group()");
}
Group::~Group()
{
delete w_attrib_path;
ass_v.clear();
ass_m.clear();
for (size_t i = 0; i < group_v.size(); i++) delete group_v[i];
group_v.clear();
group_m.clear();
impmods_v.clear();
friendmods_v.clear();
delete id;
}
Group* Group::clone() const
{
FATAL_ERROR("Ttcn::Group::clone()");
}
void Group::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
for(size_t i = 0; i < group_v.size() ; i++)
{
group_v[i]->set_fullname(p_fullname + ".<group " + Int2string(i) + ">");
}
if (w_attrib_path) w_attrib_path->set_fullname( p_fullname
+ ".<attribpath>");
}
void Group::add_ass(Definition* p_ass)
{
ass_v.add(p_ass);
p_ass->set_parent_group(this);
}
void Group::add_group(Group* p_group)
{
group_v.add(p_group);
}
void Group::set_parent_group(Group* p_parent_group)
{
if (parent_group) FATAL_ERROR("Group::set_parent_group()");
parent_group = p_parent_group;
}
void Group::set_with_attr(MultiWithAttrib* p_attrib)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_with_attr(p_attrib);
}
WithAttribPath* Group::get_attrib_path()
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
return w_attrib_path;
}
void Group::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_parent(p_path);
}
void Group::chk_uniq()
{
if (checked) return;
ass_m.clear();
group_m.clear();
for (size_t i = 0; i < ass_v.size(); i++) {
Definition *ass = ass_v[i];
const string& ass_name = ass->get_id().get_name();
if (!ass_m.has_key(ass_name)) ass_m.add(ass_name, ass);
}
for(size_t i = 0; i < group_v.size(); i++) {
Group *group = group_v[i];
const Identifier& group_id = group->get_id();
const string& group_name = group_id.get_name();
if (ass_m.has_key(group_name)) {
group->error("Group name `%s' clashes with a definition",
group_id.get_dispname().c_str());
ass_m[group_name]->note("Definition of `%s' is here",
group_id.get_dispname().c_str());
}
if (group_m.has_key(group_name)) {
group->error("Duplicate group with name `%s'",
group_id.get_dispname().c_str());
group_m[group_name]->note("Group `%s' is already defined here",
group_id.get_dispname().c_str());
} else group_m.add(group_name, group);
}
checked = true;
}
void Group::chk()
{
Error_Context cntxt(this, "In group `%s'", id->get_dispname().c_str());
chk_uniq();
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
for(size_t i = 0; i < group_v.size(); i++) group_v[i]->chk();
}
void Group::add_impmod(ImpMod *p_impmod)
{
impmods_v.add(p_impmod);
p_impmod->set_parent_group(this);
}
void Group::add_friendmod(FriendMod *p_friendmod)
{
friendmods_v.add(p_friendmod);
p_friendmod->set_parent_group(this);
}
void Group::dump(unsigned level) const
{
DEBUG(level, "Group: %s", id->get_dispname().c_str());
DEBUG(level + 1, "Nested groups: (%lu pcs.)",
static_cast<unsigned long>( group_v.size() ));
for(size_t i = 0; i < group_v.size(); i++) group_v[i]->dump(level + 2);
DEBUG(level + 1, "Nested definitions: (%lu pcs.)",
static_cast<unsigned long>( ass_v.size() ));
for(size_t i = 0; i < ass_v.size(); i++) ass_v[i]->dump(level + 2);
DEBUG(level + 1, "Nested imports: (%lu pcs.)",
static_cast<unsigned long>( impmods_v.size() ));
for(size_t i = 0; i < impmods_v.size(); i++) impmods_v[i]->dump(level + 2);
if (w_attrib_path) {
MultiWithAttrib *attrib = w_attrib_path->get_with_attr();
if (attrib) {
DEBUG(level + 1, "Group Attributes:");
attrib->dump(level + 2);
}
}
}
// =================================
// ===== ControlPart
// =================================
ControlPart::ControlPart(StatementBlock* p_block)
: Node(), Location(), block(p_block), w_attrib_path(0)
{
if (!p_block) FATAL_ERROR("ControlPart::ControlPart()");
}
ControlPart::~ControlPart()
{
delete block;
delete w_attrib_path;
}
ControlPart* ControlPart::clone() const
{
FATAL_ERROR("ControlPart::clone");
}
void ControlPart::set_fullname(const string& p_fullname)
{
block->set_fullname(p_fullname);
if(w_attrib_path) w_attrib_path->set_fullname(p_fullname + ".<attribpath>");
}
void ControlPart::set_my_scope(Scope *p_scope)
{
bridgeScope.set_parent_scope(p_scope);
string temp("control");
bridgeScope.set_scopeMacro_name(temp);
block->set_my_scope(&bridgeScope);
}
void ControlPart::chk()
{
Error_Context cntxt(this, "In control part");
block->chk();
if (!semantic_check_only)
block->set_code_section(GovernedSimple::CS_INLINE);
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
void ControlPart::generate_code(output_struct *target, Module *my_module)
{
const char *module_dispname = my_module->get_modid().get_dispname().c_str();
target->functions.control =
create_location_object(target->functions.control, "CONTROLPART",
module_dispname);
target->functions.control = mputprintf(target->functions.control,
"TTCN_Runtime::begin_controlpart(\"%s\");\n", module_dispname);
if (debugger_active) {
target->functions.control = mputprintf(target->functions.control,
"charstring_list no_params = NULL_VALUE;\n"
"TTCN3_Debug_Function debug_scope(NULL, \"control\", \"%s\", no_params, no_params, NULL);\n"
"debug_scope.initial_snapshot();\n", module_dispname);
}
target->functions.control =
block->generate_code(target->functions.control, target->header.global_vars,
target->source.global_vars);
target->functions.control = mputstr(target->functions.control,
"TTCN_Runtime::end_controlpart();\n");
}
void ControlPart::set_with_attr(MultiWithAttrib* p_attrib)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_with_attr(p_attrib);
}
WithAttribPath* ControlPart::get_attrib_path()
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
return w_attrib_path;
}
void ControlPart::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_parent(p_path);
block->set_parent_path(w_attrib_path);
}
void ControlPart::dump(unsigned level) const
{
DEBUG(level, "Control part");
block->dump(level + 1);
if (w_attrib_path) {
MultiWithAttrib *attrib = w_attrib_path->get_with_attr();
if (attrib) {
DEBUG(level + 1, "Attributes:");
attrib->dump(level + 2);
}
}
}
// =================================
// ===== Module
// =================================
Module::Module(Identifier *p_modid)
: Common::Module(MOD_TTCN, p_modid), language_spec(0), w_attrib_path(0),
controlpart(0)
{
asss = new Definitions();
asss->set_parent_scope(this);
imp = new Imports();
imp->set_my_mod(this);
//modified_encodings = true; // Assume always true for TTCN modules
}
Module::~Module()
{
delete language_spec;
delete asss;
for (size_t i = 0; i < group_v.size(); i++) delete group_v[i];
group_v.clear();
group_m.clear();
delete imp;
for (size_t i = 0; i < friendmods_v.size(); i++) delete friendmods_v[i];
friendmods_v.clear();
delete controlpart;
for (size_t i = 0; i < runs_on_scopes.size(); i++)
delete runs_on_scopes[i];
runs_on_scopes.clear();
for (size_t i = 0; i < port_scopes.size(); i++)
delete port_scopes[i];
port_scopes.clear();
delete w_attrib_path;
}
void Module::add_group(Group* p_group)
{
group_v.add(p_group);
}
void Module::add_friendmod(FriendMod *p_friendmod)
{
friendmods_v.add(p_friendmod);
p_friendmod->set_my_mod(this);
}
Module *Module::clone() const
{
FATAL_ERROR("Ttcn::Module::clone()");
}
Common::Assignment *Module::importAssignment(const Identifier& p_modid,
const Identifier& p_id) const
{
if (asss->has_local_ass_withId(p_id)) {
Common::Assignment* ass = asss->get_local_ass_byId(p_id);
if (!ass) FATAL_ERROR("Ttcn::Module::importAssignment()");
switch(ass->get_visibility()) {
case FRIEND:
for (size_t i = 0; i < friendmods_v.size(); i++) {
if (friendmods_v[i]->get_modid() == p_modid) return ass;
}
return 0;
case PUBLIC:
return ass;
case PRIVATE:
return 0;
default:
FATAL_ERROR("Ttcn::Module::importAssignment()");
return 0;
}
} else return 0;
}
void Module::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
asss->set_fullname(p_fullname);
if (controlpart) controlpart->set_fullname(p_fullname + ".control");
for(size_t i = 0; i < group_v.size(); i++)
{
group_v[i]->set_fullname(p_fullname + ".<group " + Int2string(i) + ">");
}
if (w_attrib_path) w_attrib_path->set_fullname(p_fullname
+ ".<attribpath>");
}
Common::Assignments *Module::get_scope_asss()
{
return asss;
}
bool Module::has_imported_ass_withId(const Identifier& p_id)
{
const Location *loc = NULL;
for (size_t i = 0, num = imp->get_imports_size(); i < num; ++i) {
const ImpMod* im = imp->get_impmod(i);
//TODO use a reference instead of an identifier
if(im->has_imported_def(*modid, p_id, loc)) return true;
}
return false;
}
Common::Assignment* Module::get_ass_bySRef(Ref_simple *p_ref)
{
if (p_ref->get_reftype() != Ref_simple::REF_BASIC) {
p_ref->error("Reference to `%s' can only be used inside a class definition",
p_ref->get_reftype() == Ref_simple::REF_THIS ? "this" : "super");
return NULL;
}
const Identifier *r_modid = p_ref->get_modid();
const Identifier *r_id = p_ref->get_id();
if (r_modid) {
// the reference contains a module name
if (r_modid->get_name() != modid->get_name()) {
// the reference points to another module
bool has_impmod_with_name = false;
Common::Assignment* result_ass = NULL;
for (size_t i = 0, num = imp->get_imports_size(); i < num; ++i) {
const ImpMod* im = imp->get_impmod(i);
const Identifier& im_id = im->get_modid();
const string& im_name = im_id.get_name();
if (r_modid->get_name() == im_name) {
has_impmod_with_name = true;
vector<ImpMod> tempusedImpMods;
ReferenceChain* referencechain = new ReferenceChain(this, "NEW IMPORT REFERNCECHAIN");
Common::Assignment *t_ass = im->get_imported_def(*modid, *r_id, p_ref, referencechain, tempusedImpMods);
referencechain->reset();
delete referencechain;
if (t_ass != NULL) {
Ttcn::Module *ttcn_m = static_cast<Ttcn::Module*>(im->get_mod());
if (!ttcn_m->is_visible(*modid, t_ass->get_visibility())) {
t_ass = NULL;
}
if (t_ass != NULL) {
if (result_ass == NULL) {
result_ass = t_ass;
} else if(result_ass != t_ass) {
p_ref->error(
"It is not possible to resolve the reference unambiguously"
", as it can be resolved to `%s' and to `%s'",
result_ass->get_fullname().c_str(), t_ass->get_fullname().c_str());
}
}
}
tempusedImpMods.clear();
}
}
if (result_ass) return result_ass;
if (has_impmod_with_name) {
p_ref->error("There is no definition with name `%s' visible from "
"module `%s'", r_id->get_dispname().c_str(),
r_modid->get_dispname().c_str());
} else {
if (modules->has_mod_withId(*r_modid)) {
Common::Module *m = modules->get_mod_byId(*r_modid);
if (m->get_asss()->has_ass_withId(*r_id)) {
p_ref->error("Definition with name `%s' is not imported from "
"module `%s'", r_id->get_dispname().c_str(),
r_modid->get_dispname().c_str());
} else {
p_ref->error("There is no definition with name `%s' in "
"module `%s'", r_id->get_dispname().c_str(),
r_modid->get_dispname().c_str());
}
} else {
p_ref->error("There is no module with name `%s'",
r_modid->get_dispname().c_str());
}
return 0;
}
} else {
// the reference points to the own module
if (asss->has_local_ass_withId(*r_id)) {
return asss->get_local_ass_byId(*r_id);
} else {
p_ref->error("There is no definition with name `%s' in "
"module `%s'", r_id->get_dispname().c_str(),
r_modid->get_dispname().c_str());
}
}
} else {
// no module name is given in the reference
if (asss->has_local_ass_withId(*r_id)) {
return asss->get_local_ass_byId(*r_id);
} else {
// the reference was not found locally -> look at the import list
Common::Assignment *t_result = NULL, *t_ass = NULL;
for (size_t i = 0, num = imp->get_imports_size(); i < num; ++i) {
const ImpMod* im = imp->get_impmod(i);
vector<ImpMod> tempusedImpMods;
ReferenceChain* referencechain = new ReferenceChain(this, "NEW IMPORT REFERNCECHAIN");
t_ass = im->get_imported_def(*modid, *r_id, p_ref, referencechain, tempusedImpMods);
referencechain->reset();
delete referencechain;
if (t_ass != NULL) {
Ttcn::Module *ttcn_m = static_cast<Ttcn::Module*>(im->get_mod());
if (!ttcn_m->is_visible(*modid, t_ass->get_visibility())) {
t_ass = NULL;
}
if (t_ass != NULL) {
if (t_result == NULL) {
t_result = t_ass;
} else if(t_result != t_ass) {
p_ref->error(
"It is not possible to resolve the reference unambiguously"
", as it can be resolved to `%s' and to `%s'",
t_result->get_fullname().c_str(), t_ass->get_fullname().c_str());
}
}
}
tempusedImpMods.clear();
}
if (t_result) return t_result;
p_ref->error("There is no local or imported definition with name `%s'"
,r_id->get_dispname().c_str());
}
}
return 0;
}
bool Module::is_valid_moduleid(const Identifier& p_id)
{
// The identifier represents the current module
if (p_id.get_name() == modid->get_name()) return true;
// The identifier represents a module imported in the current module
if(imp->has_impmod_withId(p_id)) return true;
return false;
}
Common::Assignments *Module::get_asss()
{
return asss;
}
bool Module::exports_sym(const Identifier&)
{
FATAL_ERROR("Ttcn::Module::exports_sym()");
return true;
}
Type *Module::get_address_type()
{
Identifier address_id(Identifier::ID_TTCN, string("address"));
// return NULL if address type is not defined
if (!asss->has_local_ass_withId(address_id)) return 0;
Common::Assignment *t_ass = asss->get_local_ass_byId(address_id);
if (t_ass->get_asstype() != Common::Assignment::A_TYPE)
FATAL_ERROR("Module::get_address_type(): address is not a type");
return t_ass->get_Type();
}
void Module::chk_imp(ReferenceChain& refch, vector<Common::Module>& moduleStack)
{
if (imp_checked) return;
const string& module_name = modid->get_dispname();
Error_Context backup;
Error_Context cntxt(this, "In TTCN-3 module `%s'", module_name.c_str());
imp->chk_imp(refch, moduleStack);
imp_checked = true;
asss->chk_uniq();
collect_visible_mods();
}
void Module::chk()
{
DEBUG(1, "Checking TTCN-3 module `%s'", modid->get_dispname().c_str());
Error_Context cntxt(this, "In TTCN-3 module `%s'",
modid->get_dispname().c_str());
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
// Check "extension" attributes in the module's "with" statement
MultiWithAttrib *multi = w_attrib_path->get_with_attr();
if (multi) for (size_t i = 0; i < multi->get_nof_elements(); ++i) {
const SingleWithAttrib *single = multi->get_element(i);
if (single->get_attribKeyword() != SingleWithAttrib::AT_EXTENSION) continue;
// Parse the extension attribute
// We circumvent parse_extattributes() in coding_attrib_p.y because
// it processes all attributes in the "with" statement and
// doesn't allow the removal on a case-by-case basis.
extatrs = 0;
init_coding_attrib_lex(single->get_attribSpec());
int result = coding_attrib_parse();// 0=OK, 1=error, 2=out of memory
cleanup_coding_attrib_lex();
if (result != 0) {
delete extatrs;
extatrs = 0;
continue;
}
const size_t num_parsed = extatrs->size();
for (size_t a = 0; a < num_parsed; ++a) {
Ttcn::ExtensionAttribute& ex = extatrs->get(0);
switch (ex.get_type()) {
case Ttcn::ExtensionAttribute::VERSION_TEMPLATE:
case Ttcn::ExtensionAttribute::VERSION: {
char* act_product_number;
unsigned int act_suffix, act_rel, act_patch, act_build;
char* extra_junk;
enum version_t version_type;
(void)ex.get_id(act_product_number, act_suffix, act_rel, act_patch, act_build, extra_junk, version_type);
if (release != UINT_MAX) {
ex.error("Duplicate 'version' attribute");
}
else {
product_number = mcopystr(act_product_number);
suffix = act_suffix;
release = act_rel;
patch = act_patch;
build = act_build;
extra = mcopystr(extra_junk);
this->version_type = version_type;
}
// Avoid propagating the attribute needlessly
multi->delete_element(i--);
single = 0;
break; }
case Ttcn::ExtensionAttribute::REQUIRES: {
// Imports have already been checked
char* exp_product_number;
unsigned int exp_suffix, exp_rel, exp_patch, exp_build;
char* exp_extra;
enum version_t version_type;
Common::Identifier *req_id = ex.get_id(exp_product_number,
exp_suffix, exp_rel, exp_patch, exp_build, exp_extra, version_type);
// We own req_id
if (imp->has_impmod_withId(*req_id)) {
Common::Module* m = modules->get_mod_byId(*req_id);
if (m->product_number == NULL && exp_product_number != NULL) {
ex.error("Module '%s' requires module '%s' of product %s"
", but it is not specified",
this->modid->get_dispname().c_str(), req_id->get_dispname().c_str(),
exp_product_number);
multi->delete_element(i--);
single = 0;
break;
} else if (exp_product_number == NULL &&
m->product_number != NULL && strlen(m->product_number) > 0){
ex.warning("Module '%s' requires module '%s' of any product"
", while it specifies '%s'",
this->modid->get_dispname().c_str(),
req_id->get_dispname().c_str(), m->product_number);
} else if (m->product_number != NULL && exp_product_number != NULL) {
bool prod_match = false;
if (version_type == m->version_type) {
prod_match = (0 == strcmp(m->product_number, exp_product_number));
}
else if (version_type == LEGACY_CRL && m->version_type == LEGACY_CAX) {
prod_match = (0 == strcmp(exp_product_number, LEGACY_CRL_PRODNR_EXECUTOR) &&
0 == strcmp(m->product_number, LEGACY_CAX_PRODNR_EXECUTOR));
}
else if (version_type == LEGACY_CAX && m->version_type == LEGACY_CRL) {
prod_match = (0 == strcmp(exp_product_number, LEGACY_CAX_PRODNR_EXECUTOR) &&
0 == strcmp(m->product_number, LEGACY_CRL_PRODNR_EXECUTOR));
}
else if (version_type == LEGACY_CRL && m->version_type == DOT_SEPARATED) {
prod_match = (0 == strcmp(exp_product_number, LEGACY_CRL_PRODNR_EXECUTOR) &&
0 == strlen(m->product_number));
}
else if (version_type == DOT_SEPARATED && m->version_type == LEGACY_CRL) {
prod_match = (0 == strlen(exp_product_number) &&
0 == strcmp(m->product_number, LEGACY_CRL_PRODNR_EXECUTOR));
}
else if (version_type == LEGACY_CAX && m->version_type == DOT_SEPARATED) {
prod_match = (0 == strcmp(exp_product_number, LEGACY_CAX_PRODNR_EXECUTOR) &&
0 == strlen(m->product_number));
}
else if (version_type == DOT_SEPARATED && m->version_type == LEGACY_CAX) {
prod_match = (0 == strlen(exp_product_number) &&
0 == strcmp(m->product_number, LEGACY_CAX_PRODNR_EXECUTOR));
}
if (!prod_match) {
char *req_product_identifier =
get_product_identifier(exp_product_number,
exp_suffix, exp_rel, exp_patch, exp_build, NULL, version_type);
char *mod_product_identifier =
get_product_identifier(m->product_number,
m->suffix, m->release, m->patch, m->build, NULL, m->version_type);
ex.error("Module '%s' requires version %s of module"
" '%s', but only %s is available",
this->modid->get_dispname().c_str(), req_product_identifier,
req_id->get_dispname().c_str(), mod_product_identifier);
Free(req_product_identifier);
Free(mod_product_identifier);
multi->delete_element(i--);
single = 0;
break;
}
}
// different suffixes are always incompatible
// unless the special version number is used
if (m->suffix != exp_suffix && (m->suffix != UINT_MAX)) {
char *req_product_identifier =
get_product_identifier(exp_product_number,exp_suffix, exp_rel, exp_patch, exp_build, NULL, version_type);
char *mod_product_identifier =
get_product_identifier(m->product_number,
m->suffix, m->release, m->patch, m->build, NULL, m->version_type);
ex.error("Module '%s' requires version %s of module"
" '%s', but only %s is available",
this->modid->get_dispname().c_str(), req_product_identifier,
req_id->get_dispname().c_str(), mod_product_identifier);
Free(req_product_identifier);
Free(mod_product_identifier);
multi->delete_element(i--);
single = 0;
break;
}
if ( m->release < exp_rel
||(m->release== exp_rel && m->patch < exp_patch)
||(m->patch == exp_patch && m->build < exp_build)) {
char *mod_bld_str = buildstr(m->build);
char *exp_bld_str = buildstr(exp_build);
if (mod_bld_str==0 || exp_bld_str==0) FATAL_ERROR(
"Ttcn::Module::chk() invalid build number");
ex.error("Module '%s' requires version R%u%c%s of module"
" '%s', but only R%u%c%s is available",
this->modid->get_dispname().c_str(),
exp_rel, eri(exp_patch), exp_bld_str,
req_id->get_dispname().c_str(),
m->release, eri(m->patch), mod_bld_str);
Free(exp_bld_str);
Free(mod_bld_str);
}
} else {
single->error("No imported module named '%s'",
req_id->get_dispname().c_str());
}
multi->delete_element(i--);
single = 0;
break; }
case Ttcn::ExtensionAttribute::REQ_TITAN: {
char* exp_product_number;
unsigned int exp_suffix, exp_minor, exp_patch, exp_build;
char* exp_extra;
enum version_t version_type;
(void)ex.get_id(exp_product_number, exp_suffix, exp_minor, exp_patch, exp_build, exp_extra, version_type);
if (exp_product_number != NULL && ((version_type == LEGACY_CAX && strcmp(exp_product_number, LEGACY_CAX_PRODNR_EXECUTOR) != 0) ||
(version_type == LEGACY_CRL && strcmp(exp_product_number, LEGACY_CRL_PRODNR_EXECUTOR) != 0) ||
(version_type == DOT_SEPARATED && strlen(exp_product_number) != 0))) {
ex.error("This module needs to be compiled with TITAN, but "
" product number %s is not TITAN"
, exp_product_number);
}
if (0 == exp_suffix) {
exp_suffix = 1; // previous version number format did not list the suffix part
}
// TTCN3_MAJOR is always 1
int expected_version = exp_suffix * 1000000
+ exp_minor * 10000 + exp_patch * 100 + exp_build;
if (expected_version > TTCN3_VERSION_MONOTONE) {
char *exp_product_identifier =
get_product_identifier(exp_product_number, exp_suffix, exp_minor, exp_patch, exp_build, NULL, version_type);
char *tmp = "UNKNOWN";
switch(version_type) {
case LEGACY_CRL:
tmp = LEGACY_CRL_PRODUCT_NUMBER;
break;
case LEGACY_CAX:
tmp = LEGACY_CAX_PRODUCT_NUMBER;
break;
case DOT_SEPARATED:
tmp = PRODUCT_NUMBER;
break;
default:
// Do nothing
break;
}
ex.error("This module needs to be compiled with TITAN version"
" %s or higher; version %s detected"
, exp_product_identifier, tmp);
Free(exp_product_identifier);
}
multi->delete_element(i--);
single = 0;
break; }
case Ttcn::ExtensionAttribute::PRINTING: {
ex.error("Attribute 'printing' not allowed at module level");
multi->delete_element(i--);
single = 0;
break;
}
default:
// Let everything else propagate into the module.
// Extension attributes in the module's "with" statement
// may be impractical, but not outright erroneous.
break;
} // switch
} // next a
delete extatrs;
} // next i
}
chk_friends();
chk_groups();
asss->chk_uniq();
asss->chk();
if (controlpart) controlpart->chk();
if (control_ns && !*control_ns) { // set but empty
error("Invalid URI value for control namespace");
}
if (control_ns_prefix && !*control_ns_prefix) { // set but empty
error("Empty NCName for the control namespace prefix is not allowed");
}
// TODO proper URI and NCName validation
}
void Module::chk_friends()
{
map<string, FriendMod> friends_m;
for(size_t i = 0; i < friendmods_v.size(); i++)
{
FriendMod* temp_friend = friendmods_v[i];
const Identifier& friend_id = temp_friend->get_modid();
const string& friend_name = friend_id.get_name();
if(friends_m.has_key(friend_name))
{
temp_friend->error("Duplicate friend module with name `%s'",
friend_id.get_dispname().c_str());
friends_m[friend_name]->note("Friend module `%s' is already defined here",
friend_id.get_dispname().c_str());
} else {
friends_m.add(friend_name, temp_friend);
}
friendmods_v[i]->chk();
}
friends_m.clear();
}
/** \todo revise */
void Module::chk_groups()
{
map<string,Common::Assignment> ass_m;
for(size_t i = 0; i < asss->get_nof_asss(); i++)
{
Common::Assignment *temp_ass = asss->get_ass_byIndex(i);
if(!temp_ass->get_parent_group())
{
const string& ass_name = temp_ass->get_id().get_name();
if (!ass_m.has_key(ass_name)) ass_m.add(ass_name, temp_ass);
}
}
for(size_t i = 0; i < group_v.size(); i++)
{
const Group* group = group_v[i];
const Identifier& group_id = group->get_id();
const string& group_name = group_id.get_name();
if(ass_m.has_key(group_name))
{
group->error("Group name `%s' clashes with a definition",
group_id.get_dispname().c_str());
ass_m[group_name]->note("Definition of `%s' is here",
group_id.get_dispname().c_str());
}
if(group_m.has_key(group_name))
{
group->error("Duplicate group with name `%s'",
group_id.get_dispname().c_str());
group_m[group_name]->note("Group `%s' is already defined here",
group_id.get_dispname().c_str());
}else{
group_m.add(group_name,group_v[i]);
}
}
ass_m.clear();
for(size_t i = 0; i < group_v.size(); i++)
{
group_v[i]->chk();
}
}
void Module::get_imported_mods(module_set_t& p_imported_mods)
{
imp->get_imported_mods(p_imported_mods);
}
void Module::generate_code_internal(CodeGenHelper& cgh) {
imp->generate_code(cgh);
asss->generate_code(cgh);
if (controlpart)
controlpart->generate_code(cgh.get_outputstruct(modid->get_ttcnname()), this);
}
RunsOnScope *Module::get_runs_on_scope(Type *comptype)
{
RunsOnScope *ret_val = new RunsOnScope(comptype);
runs_on_scopes.add(ret_val);
ret_val->set_parent_scope(asss);
ret_val->chk_uniq();
return ret_val;
}
PortScope *Module::get_port_scope(Type *porttype)
{
PortScope *ret_val = new PortScope(porttype);
port_scopes.add(ret_val);
ret_val->set_parent_scope(asss);
ret_val->chk_uniq();
return ret_val;
}
void Module::dump(unsigned level) const
{
DEBUG(level, "TTCN-3 module: %s", modid->get_dispname().c_str());
level++;
if(imp) imp->dump(level);
if(asss) asss->dump(level);
for(size_t i = 0; i < group_v.size(); i++)
{
group_v[i]->dump(level);
}
if(controlpart) controlpart->dump(level);
if (w_attrib_path) {
MultiWithAttrib *attrib = w_attrib_path->get_with_attr();
if (attrib) {
DEBUG(level, "Module Attributes:");
attrib->dump(level + 1);
}
}
}
void Module::set_language_spec(const char *p_language_spec)
{
if (language_spec) FATAL_ERROR("Module::set_language_spec()");
if (p_language_spec) language_spec = new string(p_language_spec);
}
void Module::add_ass(Definition* p_ass)
{
asss->add_ass(p_ass);
}
void Module::add_impmod(ImpMod *p_impmod)
{
imp->add_impmod(p_impmod);
}
void Module::add_controlpart(ControlPart* p_controlpart)
{
if (!p_controlpart || controlpart) FATAL_ERROR("Module::add_controlpart()");
controlpart = p_controlpart;
controlpart->set_my_scope(asss);
}
void Module::set_with_attr(MultiWithAttrib* p_attrib)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_with_attr(p_attrib);
}
WithAttribPath* Module::get_attrib_path()
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
return w_attrib_path;
}
void Module::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_parent(p_path);
}
bool Module::is_visible(const Identifier& id, visibility_t visibility){
if (visibility== PUBLIC) {
return true;
}
if (visibility== FRIEND) {
for (size_t i = 0; i < friendmods_v.size(); i++) {
if (friendmods_v[i]->get_modid() == id) {
return true;
}
}
}
return false;
}
void Module::generate_json_schema(JSON_Tokenizer& json, map<Type*, JSON_Tokenizer>& json_refs)
{
// add a new property for this module
json.put_next_token(JSON_TOKEN_NAME, modid->get_ttcnname().c_str());
// add type definitions into an object
json.put_next_token(JSON_TOKEN_OBJECT_START);
// cycle through each type, generate schema segment and reference when needed
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Def_Type* def = dynamic_cast<Def_Type*>(asss->get_ass_byIndex(i));
if (def != NULL) {
Type* t = def->get_Type();
if (t->has_encoding(Type::CT_JSON)) {
// insert type's schema segment
t->generate_json_schema(json, false, false);
if (json_refs_for_all_types && !json_refs.has_key(t)) {
// create JSON schema reference for the type
JSON_Tokenizer* json_ref = new JSON_Tokenizer;
json_refs.add(t, json_ref);
t->generate_json_schema_ref(*json_ref);
}
}
}
}
// end of type definitions
json.put_next_token(JSON_TOKEN_OBJECT_END);
// insert function data
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Def_ExtFunction* def = dynamic_cast<Def_ExtFunction*>(asss->get_ass_byIndex(i));
if (def != NULL) {
def->generate_json_schema_ref(json_refs);
}
}
}
void Module::generate_debugger_init(output_struct* output)
{
static boolean first = TRUE;
// create the initializer function
output->source.global_vars = mputprintf(output->source.global_vars,
"\n/* Initializing the TTCN-3 debugger */\n"
"void init_ttcn3_debugger()\n"
"{\n"
"%s", first ? " ttcn3_debugger.activate();\n" : "");
first = FALSE;
// initialize global scope and variables (including imported variables)
char* str_glob = generate_debugger_global_vars(NULL, this);
for (size_t i = 0; i < imp->get_imports_size(); ++i) {
str_glob = imp->get_impmod(i)->get_mod()->generate_debugger_global_vars(str_glob, this);
}
if (str_glob != NULL) {
// only add the global scope if it actually has variables
output->source.global_vars = mputprintf(output->source.global_vars,
" /* global variables */\n"
" TTCN3_Debug_Scope* global_scope = ttcn3_debugger.add_global_scope(\"%s\");\n"
"%s",
get_modid().get_dispname().c_str(), str_glob);
Free(str_glob);
}
// initialize components' scopes and their variables
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Def_Type* def = dynamic_cast<Def_Type*>(asss->get_ass_byIndex(i));
if (def != NULL) {
Type* comp_type = def->get_Type();
if (comp_type->get_typetype() == Type::T_COMPONENT) {
char* str_comp = NULL;
ComponentTypeBody* comp_body = comp_type->get_CompBody();
for (size_t j = 0; j < comp_body->get_nof_asss(); ++j) {
str_comp = generate_code_debugger_add_var(str_comp, comp_body->get_ass_byIndex(j),
this, comp_type->get_dispname().c_str());
}
if (str_comp != NULL) {
// only add the component if it actually has variables
output->source.global_vars = mputprintf(output->source.global_vars,
" /* variables of component %s */\n"
" TTCN3_Debug_Scope* %s_scope = ttcn3_debugger.add_component_scope(\"%s\");\n"
"%s"
, comp_type->get_dispname().c_str(), comp_type->get_dispname().c_str()
, comp_type->get_dispname().c_str(), str_comp);
Free(str_comp);
}
}
}
}
// close the initializer function
output->source.global_vars = mputstr(output->source.global_vars, "}\n");
}
char* Module::generate_debugger_global_vars(char* str, Common::Module* current_mod)
{
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Common::Assignment* ass = asss->get_ass_byIndex(i);
switch (ass->get_asstype()) {
case Common::Assignment::A_TEMPLATE:
if (ass->get_FormalParList() != NULL) {
// don't add parameterized templates, since they are functions in C++
break;
}
// else fall through
case Common::Assignment::A_CONST:
case Common::Assignment::A_MODULEPAR:
case Common::Assignment::A_MODULEPAR_TEMP:
str = generate_code_debugger_add_var(str, ass, current_mod, "global");
break;
case Common::Assignment::A_EXT_CONST: {
Def_ExtConst* def = dynamic_cast<Def_ExtConst*>(ass);
if (def == NULL) {
FATAL_ERROR("Module::generate_debugger_global_vars");
}
if (def->is_used()) {
str = generate_code_debugger_add_var(str, ass, current_mod, "global");
}
break; }
default:
break;
}
}
return str;
}
void Module::generate_debugger_functions(output_struct *output)
{
char* print_str = NULL;
char* overwrite_str = NULL;
for (size_t i = 0; i < asss->get_nof_asss(); ++i) {
Def_Type* def = dynamic_cast<Def_Type*>(asss->get_ass_byIndex(i));
if (def != NULL) {
Type* t = def->get_Type();
if (!t->is_ref() && t->get_typetype() != Type::T_COMPONENT &&
t->get_typetype() != Type::T_PORT) {
// don't generate code for subtypes
if (t->get_typetype() != Type::T_SIGNATURE) {
print_str = mputprintf(print_str,
" %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
" ((const %s*)ptr)->log();\n"
" }\n"
, (print_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
overwrite_str = mputprintf(overwrite_str,
" %sif (!strcmp(p_var.type_name, \"%s\")) {\n"
" ((%s*)p_var.value)->set_param(p_new_value);\n"
" }\n"
, (overwrite_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
}
print_str = mputprintf(print_str,
" %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
" ((const %s_template*)ptr)->log();\n"
" }\n"
, (print_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
if (t->get_typetype() != Type::T_SIGNATURE) {
overwrite_str = mputprintf(overwrite_str,
" %sif (!strcmp(p_var.type_name, \"%s template\")) {\n"
" ((%s_template*)p_var.value)->set_param(p_new_value);\n"
" }\n"
, (overwrite_str != NULL) ? "else " : ""
, t->get_dispname().c_str(), t->get_genname_value(this).c_str());
}
}
}
}
if (print_str != NULL) {
// don't generate an empty printing function
output->header.class_defs = mputprintf(output->header.class_defs,
"/* Debugger printing and overwriting functions for types declared in this module */\n\n"
"extern CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var);\n",
get_modid().get_ttcnname().c_str());
output->source.global_vars = mputprintf(output->source.global_vars,
"\n/* Debugger printing function for types declared in this module */\n"
"CHARSTRING print_var_%s(const TTCN3_Debugger::variable_t& p_var)\n"
"{\n"
" const void* ptr = p_var.set_function != NULL ? p_var.value : p_var.cvalue;\n"
" TTCN_Logger::begin_event_log2str();\n"
"%s"
" else {\n"
" TTCN_Logger::log_event_str(\"<unrecognized value or template>\");\n"
" }\n"
" return TTCN_Logger::end_event_log2str();\n"
"}\n", get_modid().get_ttcnname().c_str(), print_str);
Free(print_str);
}
if (overwrite_str != NULL) {
// don't generate an empty overwriting function
output->header.class_defs = mputprintf(output->header.class_defs,
"extern boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value);\n",
get_modid().get_ttcnname().c_str());
output->source.global_vars = mputprintf(output->source.global_vars,
"\n/* Debugger overwriting function for types declared in this module */\n"
"boolean set_var_%s(TTCN3_Debugger::variable_t& p_var, Module_Param& p_new_value)\n"
"{\n"
"%s"
" else {\n"
" return FALSE;\n"
" }\n"
" return TRUE;\n"
"}\n", get_modid().get_ttcnname().c_str(), overwrite_str);
Free(overwrite_str);
}
}
// =================================
// ===== Definition
// =================================
string Definition::get_genname() const
{
if (!genname.empty()) return genname;
else return id->get_name();
}
namedbool Definition::has_implicit_omit_attr() const {
if (w_attrib_path) {
const vector<SingleWithAttrib>& real_attribs =
w_attrib_path->get_real_attrib();
for (size_t in = real_attribs.size(); in > 0; in--) {
if (SingleWithAttrib::AT_OPTIONAL ==
real_attribs[in-1]->get_attribKeyword()) {
if ("implicit omit" ==
real_attribs[in-1]->get_attribSpec().get_spec()) {
return IMPLICIT_OMIT;
} else if ("explicit omit" ==
real_attribs[in-1]->get_attribSpec().get_spec()) {
return NOT_IMPLICIT_OMIT;
} // error reporting for other values is in chk_global_attrib
}
}
}
return NOT_IMPLICIT_OMIT;
}
Definition::~Definition()
{
delete w_attrib_path;
delete erroneous_attrs;
}
void Definition::set_fullname(const string& p_fullname)
{
Common::Assignment::set_fullname(p_fullname);
if (w_attrib_path) w_attrib_path->set_fullname(p_fullname + ".<attribpath>");
if (erroneous_attrs) erroneous_attrs->set_fullname(p_fullname+".<erroneous_attributes>");
}
bool Definition::is_local() const
{
if (!my_scope) FATAL_ERROR("Definition::is_local()");
for (Scope *scope = my_scope; scope; scope = scope->get_parent_scope()) {
if (dynamic_cast<StatementBlock*>(scope)) return true;
}
return false;
}
bool Definition::chk_identical(Definition *)
{
FATAL_ERROR("Definition::chk_identical()");
return false;
}
ErroneousAttributes* Definition::chk_erroneous_attr(WithAttribPath* p_attrib_path, Type* p_type,
Scope* p_scope, string p_fullname,
bool in_update_stmt)
{
if (!p_attrib_path) return NULL;
const Ttcn::MultiWithAttrib* attribs = p_attrib_path->get_local_attrib();
if (!attribs) return NULL;
ErroneousAttributes* erroneous_attrs = NULL;
for (size_t i = 0; i < attribs->get_nof_elements(); i++) {
const Ttcn::SingleWithAttrib* act_attr = attribs->get_element(i);
if (act_attr->get_attribKeyword()==Ttcn::SingleWithAttrib::AT_ERRONEOUS) {
if (!use_runtime_2) {
attribs->error("`erroneous' attributes can be used only with the Function Test Runtime");
attribs->note("If you need negative testing use the -R flag when generating the makefile");
return NULL;
}
size_t nof_qualifiers = act_attr->get_attribQualifiers() ? act_attr->get_attribQualifiers()->get_nof_qualifiers() : 0;
dynamic_array<Type*> refd_type_array(nof_qualifiers); // only the qualifiers pointing to existing fields will be added to erroneous_attrs objects
if (nof_qualifiers==0) {
act_attr->error("At least one qualifier must be specified for the `erroneous' attribute");
} else {
// check if qualifiers point to existing fields
for (size_t qi=0; qi<nof_qualifiers; qi++) {
Qualifier* act_qual = const_cast<Qualifier*>(act_attr->get_attribQualifiers()->get_qualifier(qi));
act_qual->set_my_scope(p_scope);
Type* field_type = p_type->get_field_type(act_qual, Type::EXPECTED_CONSTANT);
if (field_type) {
dynamic_array<size_t> subrefs_array;
dynamic_array<Type*> type_array;
bool valid_indexes = p_type->get_subrefs_as_array(act_qual, subrefs_array, type_array);
if (!valid_indexes) field_type = NULL;
if (act_qual->refers_to_string_element()) {
act_qual->error("Reference to a string element cannot be used in this context");
field_type = NULL;
}
}
refd_type_array.add(field_type);
}
}
// parse the attr. spec.
ErroneousAttributeSpec* err_attr_spec = ttcn3_parse_erroneous_attr_spec_string(
act_attr->get_attribSpec().get_spec().c_str(), act_attr->get_attribSpec());
if (err_attr_spec) {
if (!erroneous_attrs) erroneous_attrs = new ErroneousAttributes(p_type);
// attr.spec will be owned by erroneous_attrs object
erroneous_attrs->add_spec(err_attr_spec);
err_attr_spec->set_fullname(p_fullname);
err_attr_spec->set_my_scope(p_scope);
err_attr_spec->chk(in_update_stmt);
// create qualifier - err.attr.spec. pairs
for (size_t qi=0; qi<nof_qualifiers; qi++) {
if (refd_type_array[qi] && (err_attr_spec->get_indicator()!=ErroneousAttributeSpec::I_INVALID)) {
erroneous_attrs->add_pair(act_attr->get_attribQualifiers()->get_qualifier(qi), err_attr_spec);
}
}
}
}
}
if (erroneous_attrs) erroneous_attrs->chk();
return erroneous_attrs;
}
char* Definition::generate_code_str(char *str)
{
FATAL_ERROR("Definition::generate_code_str()");
return str;
}
void Definition::ilt_generate_code(ILT *)
{
FATAL_ERROR("Definition::ilt_generate_code()");
}
char *Definition::generate_code_init_comp(char *str, Definition *)
{
FATAL_ERROR("Definition::generate_code_init_comp()");
return str;
}
void Definition::set_with_attr(MultiWithAttrib* p_attrib)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_with_attr(p_attrib);
}
WithAttribPath* Definition::get_attrib_path()
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
return w_attrib_path;
}
void Definition::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) w_attrib_path = new WithAttribPath();
w_attrib_path->set_parent(p_path);
}
void Definition::set_parent_group(Group* p_group)
{
if(parentgroup) // there would be a leak!
FATAL_ERROR("Definition::set_parent_group()");
parentgroup = p_group;
}
Group* Definition::get_parent_group()
{
return parentgroup;
}
void Definition::dump_internal(unsigned level) const
{
DEBUG(level, "Move along, nothing to see here");
}
void Definition::dump(unsigned level) const
{
dump_internal(level);
if (w_attrib_path) {
MultiWithAttrib *attrib = w_attrib_path->get_with_attr();
if (attrib) {
DEBUG(level + 1, "Definition Attributes:");
attrib->dump(level + 2);
}
}
if (erroneous_attrs) erroneous_attrs->dump(level+1);
}
// =================================
// ===== Def_Type
// =================================
Def_Type::Def_Type(Identifier *p_id, Type *p_type)
: Definition(A_TYPE, p_id), type(p_type)
{
if(!p_type) FATAL_ERROR("Ttcn::Def_Type::Def_Type()");
type->set_ownertype(Type::OT_TYPE_DEF, this);
}
Def_Type::~Def_Type()
{
delete type;
}
Def_Type *Def_Type::clone() const
{
FATAL_ERROR("Def_Type::clone");
}
void Def_Type::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname);
}
void Def_Type::set_my_scope(Scope *p_scope)
{
bridgeScope.set_parent_scope(p_scope);
bridgeScope.set_scopeMacro_name(id->get_dispname());
Definition::set_my_scope(&bridgeScope);
type->set_my_scope(&bridgeScope);
}
Setting *Def_Type::get_Setting()
{
return get_Type();
}
Type *Def_Type::get_Type()
{
chk();
return type;
}
void Def_Type::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In %s definition `%s'",
type->get_typetype() == Type::T_SIGNATURE ? "signature" : "type",
id->get_dispname().c_str());
type->set_genname(get_genname());
if (!semantic_check_only && type->get_typetype() == Type::T_COMPONENT) {
// the prefix of embedded definitions must be set before the checking
type->get_CompBody()->set_genname(get_genname() + "_component_");
}
while (w_attrib_path) { // not a loop, but we can _break_ out of it
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
if (type->get_typetype() != Type::T_ANYTYPE) break;
// This is the anytype; it must be empty (we're about to add the fields)
if (type->get_nof_comps() > 0) FATAL_ERROR("Def_Type::chk");
Ttcn::ExtensionAttributes *extattrs = parse_extattributes(w_attrib_path);
if (extattrs == 0) break; // NULL means parsing error
size_t num_atrs = extattrs->size();
for (size_t k = 0; k < num_atrs; ++k) {
ExtensionAttribute &ea = extattrs->get(k);
switch (ea.get_type()) {
case ExtensionAttribute::ANYTYPELIST: {
Types *anytypes = ea.get_types();
// List of types to be converted into fields for the anytype.
// Make sure scope is set on all types in the list.
anytypes->set_my_scope(get_my_scope());
// Convert the list of types into field names for the anytype
for (size_t i=0; i < anytypes->get_nof_types(); ++i) {
Type *t = anytypes->extract_type_byIndex(i);
// we are now the owner of the Type.
if (t->get_typetype()==Type::T_ERROR) { // should we give up?
delete t;
continue;
}
string field_name;
const char* btn = Type::get_typename_builtin(t->get_typetype());
if (btn) {
field_name = btn;
}
else if (t->get_typetype() == Type::T_REFD) {
// Extract the identifier
Common::Reference *ref = t->get_Reference();
Ttcn::Reference *tref = dynamic_cast<Ttcn::Reference*>(ref);
if (!tref) FATAL_ERROR("Def_Type::chk, wrong kind of reference");
const Common::Identifier *modid = tref->get_modid();
if (modid) {
ea.error("Qualified name '%s' cannot be added to the anytype",
tref->get_dispname().c_str());
delete t;
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
FATAL_ERROR("Unexpected type %d", t->get_typetype());
}
const string& at_field = anytype_field(field_name);
Identifier *field_id = new Identifier(Identifier::ID_TTCN, at_field);
CompField *cf = new CompField(field_id, t, false, 0);
cf->set_location(ea);
cf->set_fullname(get_fullname());
type->add_comp(cf);
} // next i
delete anytypes;
break; }
default:
w_attrib_path->get_with_attr()->error("Type def can only have anytype");
break;
} // switch
} // next attribute
delete extattrs;
break; // do not loop
}
// Now we can check the type
type->chk();
type->chk_constructor_name(*id);
if (id->get_ttcnname() == "address") type->chk_address();
ReferenceChain refch(type, "While checking embedded recursions");
type->chk_recursions(refch);
if (type->get_typetype()==Type::T_FUNCTION
||type->get_typetype()==Type::T_ALTSTEP
||type->get_typetype()==Type::T_TESTCASE) {
// TR 922. This is a function/altstep/testcase reference.
// Set this definition as the definition for the formal parameters.
type->get_fat_parameters()->set_my_def(this);
}
}
void Def_Type::generate_code(output_struct *target, bool)
{
type->generate_code(target);
if (type->get_typetype() == Type::T_COMPONENT) {
// the C++ equivalents of embedded component element definitions must be
// generated from outside Type::generate_code() because the function can
// call itself recursively and create invalid (overlapped) initializer
// sequences
type->get_CompBody()->generate_code(target);
}
}
void Def_Type::generate_code(CodeGenHelper& cgh) {
type->generate_code(cgh.get_outputstruct(get_Type()));
if (type->get_typetype() == Type::T_COMPONENT) {
// the C++ equivalents of embedded component element definitions must be
// generated from outside Type::generate_code() because the function can
// call itself recursively and create invalid (overlapped) initializer
// sequences
type->get_CompBody()->generate_code(cgh.get_current_outputstruct());
}
cgh.finalize_generation(get_Type());
}
void Def_Type::dump_internal(unsigned level) const
{
DEBUG(level, "Type def: %s @ %p", id->get_dispname().c_str(), static_cast<const void*>(this));
type->dump(level + 1);
}
void Def_Type::set_with_attr(MultiWithAttrib* p_attrib)
{
if (!w_attrib_path) {
w_attrib_path = new WithAttribPath();
type->set_parent_path(w_attrib_path);
}
type->set_with_attr(p_attrib);
}
WithAttribPath* Def_Type::get_attrib_path()
{
if (!w_attrib_path) {
w_attrib_path = new WithAttribPath();
type->set_parent_path(w_attrib_path);
}
return w_attrib_path;
}
void Def_Type::set_parent_path(WithAttribPath* p_path)
{
if (!w_attrib_path) {
w_attrib_path = new WithAttribPath();
type->set_parent_path(w_attrib_path);
}
w_attrib_path->set_parent(p_path);
}
// =================================
// ===== Def_Const
// =================================
Def_Const::Def_Const(Identifier *p_id, Type *p_type, Value *p_value)
: Definition(A_CONST, p_id)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_Const::Def_Const()");
type=p_type;
type->set_ownertype(Type::OT_CONST_DEF, this);
value=p_value;
value_under_check=false;
}
Def_Const::~Def_Const()
{
delete type;
delete value;
}
Def_Const *Def_Const::clone() const
{
FATAL_ERROR("Def_Const::clone");
}
void Def_Const::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
if (value != NULL) {
value->set_fullname(p_fullname);
}
}
void Def_Const::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type->set_my_scope(p_scope);
if (value != NULL) {
value->set_my_scope(p_scope);
}
}
Setting *Def_Const::get_Setting()
{
return get_Value();
}
Type *Def_Const::get_Type()
{
chk();
return type;
}
Value *Def_Const::get_Value()
{
chk();
return value;
}
void Def_Const::chk()
{
if(checked) {
if (value_under_check) {
error("Circular reference in constant definition `%s'",
id->get_dispname().c_str());
value_under_check = false; // only report the error once for this definition
}
return;
}
Error_Context cntxt(this, "In constant definition `%s'",
id->get_dispname().c_str());
type->set_genname(_T_, get_genname());
type->chk();
if (value != NULL) {
value->set_my_governor(type);
type->chk_this_value_ref(value);
}
else if (!my_scope->is_class_scope()) {
error("Missing initial value");
}
checked=true;
if (w_attrib_path) {
w_attrib_path->chk_global_attrib(true);
switch (type->get_type_refd_last()->get_typetype_ttcn3()) {
case Type::T_SEQ_T:
case Type::T_SET_T:
case Type::T_CHOICE_T:
// These types may have qualified attributes
break;
case Type::T_SEQOF: case Type::T_SETOF:
break;
default:
w_attrib_path->chk_no_qualif();
break;
}
}
Type *t = type->get_type_refd_last();
switch (t->get_typetype()) {
case Type::T_PORT:
error("Constant cannot be defined for port type `%s'",
t->get_fullname().c_str());
break;
case Type::T_SIGNATURE:
error("Constant cannot be defined for signature `%s'",
t->get_fullname().c_str());
break;
case Type::T_CLASS:
error("Constant cannot be defined for class type `%s'",
t->get_typename().c_str());
break;
default:
if (value != NULL) {
value_under_check = true;
namedbool class_member_init = my_scope->is_class_scope() ?
CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT;
type->chk_this_value(value, 0, Type::EXPECTED_STATIC_VALUE, WARNING_FOR_INCOMPLETE,
OMIT_NOT_ALLOWED, SUB_CHK, has_implicit_omit_attr(), NOT_STR_ELEM, class_member_init);
value_under_check = false;
erroneous_attrs = chk_erroneous_attr(w_attrib_path, type, get_my_scope(),
get_fullname(), false);
if (erroneous_attrs) value->add_err_descr(NULL, erroneous_attrs->get_err_descr());
ReferenceChain refch(type, "While checking embedded recursions");
value->chk_recursions(refch);
}
break;
}
if (!semantic_check_only && value != NULL) {
value->set_genname_prefix("const_");
value->set_genname_recursive(get_genname());
value->set_code_section(my_scope->is_class_scope() ?
GovernedSimple::CS_INIT_CLASS : GovernedSimple::CS_PRE_INIT);
if (my_scope->is_class_scope()) {
value->chk_class_member(my_scope->get_scope_class());
}
}
}
bool Def_Const::chk_identical(Definition *p_def)
{
chk();
p_def->chk();
if (p_def->get_asstype() != A_CONST) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a constant, but the definition "
"inherited from component type `%s' is a %s", dispname_str,
p_def->get_my_scope()->get_fullname().c_str(), p_def->get_assname());
p_def->note("The inherited definition of `%s' is here", dispname_str);
return false;
}
Def_Const *p_def_const = dynamic_cast<Def_Const*>(p_def);
if (p_def_const == NULL || value == NULL || p_def_const->value == NULL) {
FATAL_ERROR("Def_Const::chk_identical()");
}
if (!type->is_identical(p_def_const->type)) {
const char *dispname_str = id->get_dispname().c_str();
type->error("Local constant `%s' has type `%s', but the constant "
"inherited from component type `%s' has type `%s'", dispname_str,
type->get_typename().c_str(),
p_def_const->get_my_scope()->get_fullname().c_str(),
p_def_const->type->get_typename().c_str());
p_def_const->note("The inherited constant `%s' is here", dispname_str);
return false;
} else if (!(*value == *p_def_const->value)) {
const char *dispname_str = id->get_dispname().c_str();
value->error("Local constant `%s' and the constant inherited from "
"component type `%s' have different values", dispname_str,
p_def_const->get_my_scope()->get_fullname().c_str());
p_def_const->note("The inherited constant `%s' is here", dispname_str);
return false;
} else return true;
}
void Def_Const::generate_code(output_struct *target, bool)
{
type->generate_code(target);
const_def cdef;
Code::init_cdef(&cdef);
bool in_class = my_scope->is_class_scope();
if (value != NULL) {
type->generate_code_object(&cdef, value, in_class);
if (value->is_unfoldable()) {
cdef.post = update_location_object(cdef.post);
cdef.post = value->generate_code_init(cdef.post,
value->get_lhs_name().c_str());
} else {
cdef.init = update_location_object(cdef.init);
cdef.init = value->generate_code_init(cdef.init,
value->get_lhs_name().c_str());
}
}
else {
type->generate_code_object(&cdef, my_scope, get_genname(), "const_", false, false, true);
}
Code::merge_cdef(target, &cdef, in_class);
Code::free_cdef(&cdef);
}
void Def_Const::generate_code(Common::CodeGenHelper& cgh) {
// constant definitions always go to its containing module
generate_code(cgh.get_current_outputstruct());
}
char *Def_Const::generate_code_str(char *str)
{
if (value == NULL) {
FATAL_ERROR("Def_Const::generate_code_str");
}
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
if (value->has_single_expr()) {
// the value can be represented by a single C++ expression
// the object is initialized by the constructor
str = mputprintf(str, "%s %s(%s);\n",
type->get_genname_value(my_scope).c_str(), genname_str,
value->get_single_expr().c_str());
} else {
// use the default constructor
str = mputprintf(str, "%s %s;\n",
type->get_genname_value(my_scope).c_str(), genname_str);
// the value is assigned using subsequent statements
str = value->generate_code_init(str, genname_str);
}
if (debugger_active) {
str = generate_code_debugger_add_var(str, this);
}
return str;
}
void Def_Const::ilt_generate_code(ILT *ilt)
{
if (value == NULL) {
FATAL_ERROR("Def_Const::ilt_generate_code");
}
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
char*& def=ilt->get_out_def();
char*& init=ilt->get_out_branches();
def = mputprintf(def, "%s %s;\n", type->get_genname_value(my_scope).c_str(),
genname_str);
init = value->generate_code_init(init, genname_str);
}
char *Def_Const::generate_code_init_comp(char *str, Definition *)
{
/* This function actually does nothing as \a this and \a base_defn are
* exactly the same. */
return str;
}
void Def_Const::dump_internal(unsigned level) const
{
DEBUG(level, "Constant: %s @%p", id->get_dispname().c_str(), static_cast<const void*>(this));
type->dump(level + 1);
if (value != NULL) {
value->dump(level + 1);
}
}
// =================================
// ===== Def_ExtConst
// =================================
Def_ExtConst::Def_ExtConst(Identifier *p_id, Type *p_type)
: Definition(A_EXT_CONST, p_id)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_ExtConst::Def_ExtConst()");
type = p_type;
type->set_ownertype(Type::OT_CONST_DEF, this);
usage_found = false;
}
Def_ExtConst::~Def_ExtConst()
{
delete type;
}
Def_ExtConst *Def_ExtConst::clone() const
{
FATAL_ERROR("Def_ExtConst::clone");
}
void Def_ExtConst::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
}
void Def_ExtConst::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type->set_my_scope(p_scope);
}
Type *Def_ExtConst::get_Type()
{
chk();
return type;
}
void Def_ExtConst::chk()
{
if(checked) return;
Error_Context cntxt(this, "In external constant definition `%s'",
id->get_dispname().c_str());
type->set_genname(_T_, get_genname());
type->chk();
checked=true;
Type *t = type->get_type_refd_last();
switch (t->get_typetype()) {
case Type::T_PORT:
error("External constant cannot be defined for port type `%s'",
t->get_fullname().c_str());
break;
case Type::T_SIGNATURE:
error("External constant cannot be defined for signature `%s'",
t->get_fullname().c_str());
break;
case Type::T_CLASS:
error("External constant cannot be defined for class type `%s'",
t->get_typename().c_str());
break;
default:
break;
}
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
switch (type->get_type_refd_last()->get_typetype()) {
case Type::T_SEQ_T:
case Type::T_SET_T:
case Type::T_CHOICE_T:
// These types may have qualified attributes
break;
case Type::T_SEQOF: case Type::T_SETOF:
break;
default:
w_attrib_path->chk_no_qualif();
break;
}
}
}
void Def_ExtConst::generate_code(output_struct *target, bool)
{
type->generate_code(target);
target->header.global_vars = mputprintf(target->header.global_vars,
"extern const %s& %s;\n", type->get_genname_value(my_scope).c_str(),
get_genname().c_str());
}
void Def_ExtConst::generate_code(Common::CodeGenHelper& cgh) {
// constant definitions always go to its containing module
generate_code(cgh.get_current_outputstruct());
}
void Def_ExtConst::dump_internal(unsigned level) const
{
DEBUG(level, "External constant: %s @ %p", id->get_dispname().c_str(), static_cast<const void*>(this));
type->dump(level + 1);
}
// =================================
// ===== Def_Modulepar
// =================================
Def_Modulepar::Def_Modulepar(Identifier *p_id, Type *p_type, Value *p_defval)
: Definition(A_MODULEPAR, p_id)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_Modulepar::Def_Modulepar()");
type = p_type;
type->set_ownertype(Type::OT_MODPAR_DEF, this);
def_value = p_defval;
}
Def_Modulepar::~Def_Modulepar()
{
delete type;
delete def_value;
}
Def_Modulepar* Def_Modulepar::clone() const
{
FATAL_ERROR("Def_Modulepar::clone");
}
void Def_Modulepar::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
if (def_value) def_value->set_fullname(p_fullname + ".<default_value>");
}
void Def_Modulepar::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type->set_my_scope(p_scope);
if (def_value) def_value->set_my_scope(p_scope);
}
Type *Def_Modulepar::get_Type()
{
chk();
return type;
}
void Def_Modulepar::chk()
{
if(checked) return;
Error_Context cntxt(this, "In module parameter definition `%s'",
id->get_dispname().c_str());
type->set_genname(_T_, get_genname());
type->chk();
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
switch (type->get_type_refd_last()->get_typetype()) {
case Type::T_SEQ_T:
case Type::T_SET_T:
case Type::T_CHOICE_T:
// These types may have qualified attributes
break;
case Type::T_SEQOF: case Type::T_SETOF:
break;
default:
w_attrib_path->chk_no_qualif();
break;
}
}
map<Type*,void> type_chain;
map<Type::typetype_t, void> not_allowed;
not_allowed.add(Type::T_PORT, 0);
Type *t = type->get_type_refd_last();
// if the type is valid the original will be returned
Type::typetype_t tt = t->search_for_not_allowed_type(type_chain, not_allowed);
type_chain.clear();
not_allowed.clear();
switch (tt) {
case Type::T_PORT:
error("Type of module parameter cannot be or embed port type `%s'",
t->get_fullname().c_str());
break;
case Type::T_SIGNATURE:
error("Type of module parameter cannot be signature `%s'",
t->get_fullname().c_str());
break;
case Type::T_CLASS:
error("Type of module parameter cannot be or embed class type `%s'",
t->get_typename().c_str());
break;
case Type::T_FUNCTION:
case Type::T_ALTSTEP:
case Type::T_TESTCASE:
if (t->get_fat_runs_on_self()) {
error("Type of module parameter cannot be of function reference type"
" `%s' which has runs on self clause", t->get_fullname().c_str());
break;
}
default:
#if defined(MINGW)
checked = true;
#else
if (def_value) {
Error_Context cntxt2(def_value, "In default value");
def_value->set_my_governor(type);
type->chk_this_value_ref(def_value);
checked = true;
type->chk_this_value(def_value, 0, Type::EXPECTED_CONSTANT, INCOMPLETE_ALLOWED,
OMIT_NOT_ALLOWED, SUB_CHK, has_implicit_omit_attr());
if (!semantic_check_only) {
def_value->set_genname_prefix("modulepar_");
def_value->set_genname_recursive(get_genname());
def_value->set_code_section(GovernedSimple::CS_PRE_INIT);
}
} else checked = true;
#endif
break;
}
}
void Def_Modulepar::generate_code(output_struct *target, bool)
{
type->generate_code(target);
const_def cdef;
Code::init_cdef(&cdef);
const string& t_genname = get_genname();
const char *name = t_genname.c_str();
type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false, false);
if (def_value) {
cdef.init = update_location_object(cdef.init);
cdef.init = def_value->generate_code_init(cdef.init, def_value->get_lhs_name().c_str());
}
Code::merge_cdef(target, &cdef);
Code::free_cdef(&cdef);
if (IMPLICIT_OMIT == has_implicit_omit_attr()) {
target->functions.post_init = mputprintf(target->functions.post_init,
"modulepar_%s.set_implicit_omit();\n", name);
}
const char *dispname = id->get_dispname().c_str();
target->functions.set_param = mputprintf(target->functions.set_param,
"if (!strcmp(par_name, \"%s\")) {\n"
"modulepar_%s.set_param(param);\n"
"return TRUE;\n"
"} else ", dispname, name);
if (use_runtime_2) {
target->functions.get_param = mputprintf(target->functions.get_param,
"if (!strcmp(par_name, \"%s\")) {\n"
"return modulepar_%s.get_param(param_name);\n"
"} else ", dispname, name);
}
if (target->functions.log_param) {
// this is not the first modulepar
target->functions.log_param = mputprintf(target->functions.log_param,
"TTCN_Logger::log_event_str(\", %s := \");\n", dispname);
} else {
// this is the first modulepar
target->functions.log_param = mputprintf(target->functions.log_param,
"TTCN_Logger::log_event_str(\"%s := \");\n", dispname);
}
target->functions.log_param = mputprintf(target->functions.log_param,
"%s.log();\n", name);
target->functions.pre_init = mputprintf(target->functions.pre_init,
"module_object.add_modulepar(\"%s\");\n", dispname);
}
void Def_Modulepar::generate_code(Common::CodeGenHelper& cgh) {
// module parameter definitions always go to its containing module
generate_code(cgh.get_current_outputstruct());
}
void Def_Modulepar::dump_internal(unsigned level) const
{
DEBUG(level, "Module parameter: %s @ %p", id->get_dispname().c_str(), static_cast<const void*>(this));
type->dump(level + 1);
if (def_value) def_value->dump(level + 1);
else DEBUG(level + 1, "No default value");
}
// =================================
// ===== Def_Modulepar_Template
// =================================
Def_Modulepar_Template::Def_Modulepar_Template(Identifier *p_id, Type *p_type, Template *p_deftmpl)
: Definition(A_MODULEPAR_TEMP, p_id)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_Modulepar_Template::Def_Modulepar_Template()");
type = p_type;
type->set_ownertype(Type::OT_MODPAR_DEF, this);
def_template = p_deftmpl;
}
Def_Modulepar_Template::~Def_Modulepar_Template()
{
delete type;
delete def_template;
}
Def_Modulepar_Template* Def_Modulepar_Template::clone() const
{
FATAL_ERROR("Def_Modulepar_Template::clone");
}
void Def_Modulepar_Template::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
if (def_template) def_template->set_fullname(p_fullname + ".<default_template>");
}
void Def_Modulepar_Template::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type->set_my_scope(p_scope);
if (def_template) def_template->set_my_scope(p_scope);
}
Type *Def_Modulepar_Template::get_Type()
{
chk();
return type;
}
void Def_Modulepar_Template::chk()
{
if(checked) return;
Error_Context cntxt(this, "In template module parameter definition `%s'",
id->get_dispname().c_str());
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
switch (type->get_type_refd_last()->get_typetype()) {
case Type::T_SEQ_T:
case Type::T_SET_T:
case Type::T_CHOICE_T:
// These types may have qualified attributes
break;
case Type::T_SEQOF: case Type::T_SETOF:
break;
default:
w_attrib_path->chk_no_qualif();
break;
}
}
type->set_genname(_T_, get_genname());
type->chk();
Type *t = type->get_type_refd_last();
switch (t->get_typetype()) {
case Type::T_PORT:
error("Type of template module parameter cannot be port type `%s'",
t->get_fullname().c_str());
break;
case Type::T_SIGNATURE:
error("Type of template module parameter cannot be signature `%s'",
t->get_fullname().c_str());
break;
case Type::T_FUNCTION:
case Type::T_ALTSTEP:
case Type::T_TESTCASE:
if (t->get_fat_runs_on_self()) {
error("Type of template module parameter cannot be of function reference type"
" `%s' which has runs on self clause", t->get_fullname().c_str());
}
break;
case Type::T_CLASS:
error("Type of template module parameter cannot be class type `%s'",
t->get_typename().c_str());
break;
default:
if (IMPLICIT_OMIT == has_implicit_omit_attr()) {
error("Implicit omit not supported for template module parameters");
}
#if defined(MINGW)
checked = true;
#else
if (def_template) {
Error_Context cntxt2(def_template, "In default template");
def_template->set_my_governor(type);
def_template->flatten(false);
if (def_template->get_templatetype() == Template::CSTR_PATTERN &&
type->get_type_refd_last()->get_typetype() == Type::T_USTR) {
def_template->set_templatetype(Template::USTR_PATTERN);
def_template->get_ustr_pattern()->set_pattern_type(
PatternString::USTR_PATTERN);
}
type->chk_this_template_ref(def_template);
checked = true;
type->chk_this_template_generic(def_template, INCOMPLETE_ALLOWED,
OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK,
IMPLICIT_OMIT == has_implicit_omit_attr() ? IMPLICIT_OMIT : NOT_IMPLICIT_OMIT,
NOT_CLASS_MEMBER_INIT, 0);
type->chk_this_template_incorrect_field();
if (!semantic_check_only) {
def_template->set_genname_prefix("modulepar_");
def_template->set_genname_recursive(get_genname());
def_template->set_code_section(GovernedSimple::CS_PRE_INIT);
}
} else checked = true;
#endif
break;
}
}
void Def_Modulepar_Template::generate_code(output_struct *target, bool)
{
type->generate_code(target);
const_def cdef;
Code::init_cdef(&cdef);
const string& t_genname = get_genname();
const char *name = t_genname.c_str();
type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false, false);
if (def_template) {
cdef.init = update_location_object(cdef.init);
cdef.init = def_template->generate_code_init(cdef.init, def_template->get_lhs_name().c_str());
}
Code::merge_cdef(target, &cdef);
Code::free_cdef(&cdef);
if (IMPLICIT_OMIT == has_implicit_omit_attr()) {
FATAL_ERROR("Def_Modulepar_Template::generate_code()");
}
const char *dispname = id->get_dispname().c_str();
target->functions.set_param = mputprintf(target->functions.set_param,
"if (!strcmp(par_name, \"%s\")) {\n"
"modulepar_%s.set_param(param);\n"
"return TRUE;\n"
"} else ", dispname, name);
target->functions.get_param = mputprintf(target->functions.get_param,
"if (!strcmp(par_name, \"%s\")) {\n"
"return modulepar_%s.get_param(param_name);\n"
"} else ", dispname, name);
if (target->functions.log_param) {
// this is not the first modulepar
target->functions.log_param = mputprintf(target->functions.log_param,
"TTCN_Logger::log_event_str(\", %s := \");\n", dispname);
} else {
// this is the first modulepar
target->functions.log_param = mputprintf(target->functions.log_param,
"TTCN_Logger::log_event_str(\"%s := \");\n", dispname);
}
target->functions.log_param = mputprintf(target->functions.log_param,
"%s.log();\n", name);
target->functions.pre_init = mputprintf(target->functions.pre_init,
"module_object.add_modulepar(\"%s\");\n", dispname);
}
void Def_Modulepar_Template::generate_code(Common::CodeGenHelper& cgh) {
// module parameter definitions always go to its containing module
generate_code(cgh.get_current_outputstruct());
}
void Def_Modulepar_Template::dump_internal(unsigned level) const
{
DEBUG(level, "Module parameter: %s @ %p", id->get_dispname().c_str(), static_cast<const void*>(this));
type->dump(level + 1);
if (def_template) def_template->dump(level + 1);
else DEBUG(level + 1, "No default template");
}
// =================================
// ===== Def_Template
// =================================
Def_Template::Def_Template(template_restriction_t p_template_restriction,
Identifier *p_id, Type *p_type, FormalParList *p_fpl,
Reference *p_derived_ref, Template *p_body)
: Definition(A_TEMPLATE, p_id), type(p_type), fp_list(p_fpl),
derived_ref(p_derived_ref), base_template(0), recurs_deriv_checked(false),
body(p_body), template_restriction(p_template_restriction),
gen_restriction_check(false)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_Template::Def_Template()");
type->set_ownertype(Type::OT_TEMPLATE_DEF, this);
if (fp_list) fp_list->set_my_def(this);
}
Def_Template::~Def_Template()
{
delete type;
delete fp_list;
delete derived_ref;
delete body;
}
Def_Template *Def_Template::clone() const
{
FATAL_ERROR("Def_Template::clone");
}
void Def_Template::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
if (fp_list) fp_list->set_fullname(p_fullname + ".<formal_par_list>");
if (derived_ref)
derived_ref->set_fullname(p_fullname + ".<derived_reference>");
if (body != NULL) {
body->set_fullname(p_fullname);
}
}
void Def_Template::set_my_scope(Scope *p_scope)
{
bridgeScope.set_parent_scope(p_scope);
bridgeScope.set_scopeMacro_name(id->get_dispname());
Definition::set_my_scope(&bridgeScope);
type->set_my_scope(&bridgeScope);
if (derived_ref) derived_ref->set_my_scope(&bridgeScope);
if (fp_list) {
fp_list->set_my_scope(&bridgeScope);
if (body != NULL) {
body->set_my_scope(fp_list);
}
} else if (body != NULL) {
body->set_my_scope(&bridgeScope);
}
}
Setting *Def_Template::get_Setting()
{
return get_Template();
}
Type *Def_Template::get_Type()
{
if (!checked) chk();
return type;
}
Template *Def_Template::get_Template()
{
if (!checked) chk();
return body;
}
FormalParList *Def_Template::get_FormalParList()
{
if (!checked) chk();
return fp_list;
}
void Def_Template::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In template definition `%s'",
id->get_dispname().c_str());
const string& t_genname = get_genname();
type->set_genname(_T_, t_genname);
type->chk();
if (w_attrib_path) {
w_attrib_path->chk_global_attrib(true);
switch (type->get_type_refd_last()->get_typetype_ttcn3()) {
case Type::T_SEQ_T:
case Type::T_SET_T:
case Type::T_CHOICE_T:
// These types may have qualified attributes
break;
case Type::T_SEQOF: case Type::T_SETOF:
break;
default:
w_attrib_path->chk_no_qualif();
break;
}
}
if (fp_list) {
chk_default();
fp_list->chk(asstype);
if (local_scope) error("Parameterized local template `%s' not supported",
id->get_dispname().c_str());
if (body != NULL) {
body->set_formalparlist(fp_list);
}
}
// Merge the elements of "all from" into the list
if (body != NULL) {
body->flatten(false);
body->set_my_governor(type);
if (body->get_templatetype() == Template::CSTR_PATTERN &&
type->get_type_refd_last()->get_typetype() == Type::T_USTR) {
body->set_templatetype(Template::USTR_PATTERN);
body->get_ustr_pattern()->set_pattern_type(PatternString::USTR_PATTERN);
}
type->chk_this_template_ref(body);
}
else if (!my_scope->is_class_scope()) {
error("Missing template body");
}
Type *t = type->get_type_refd_last();
if (t->get_typetype() == Type::T_PORT) {
error("Template cannot be defined for port type `%s'",
t->get_fullname().c_str());
}
else if (t->get_typetype() == Type::T_CLASS) {
error("Template cannot be defined for class type `%s'",
t->get_typename().c_str());
}
type->chk_this_template_incorrect_field();
chk_modified();
chk_recursive_derivation();
if (body != NULL) {
namedbool class_member_init = my_scope->is_class_scope() ?
CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT;
type->chk_this_template_generic(body,
derived_ref != NULL ? INCOMPLETE_ALLOWED : WARNING_FOR_INCOMPLETE,
OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK,
IMPLICIT_OMIT == has_implicit_omit_attr() ? IMPLICIT_OMIT : NOT_IMPLICIT_OMIT,
class_member_init, 0);
erroneous_attrs = chk_erroneous_attr(w_attrib_path, type, get_my_scope(),
get_fullname(), false);
if (erroneous_attrs) body->add_err_descr(NULL, erroneous_attrs->get_err_descr());
{
ReferenceChain refch(type, "While checking embedded recursions");
body->chk_recursions(refch);
}
if (template_restriction!=TR_NONE) {
Error_Context ec(this, "While checking template restriction `%s'",
Template::get_restriction_name(template_restriction));
gen_restriction_check =
body->chk_restriction("template definition", template_restriction, body);
if (fp_list && template_restriction!=TR_PRESENT) {
size_t nof_fps = fp_list->get_nof_fps();
for (size_t i=0; i<nof_fps; i++) {
FormalPar* fp = fp_list->get_fp_byIndex(i);
// if formal par is not template then skip restriction checking,
// templates can have only `in' parameters
if (fp->get_asstype()!=A_PAR_TEMPL_IN) continue;
template_restriction_t fp_tr = fp->get_template_restriction();
switch (template_restriction) {
case TR_VALUE:
case TR_OMIT:
switch (fp_tr) {
case TR_VALUE:
case TR_OMIT:
// allowed
break;
case TR_PRESENT:
fp->error("Formal parameter with template restriction `%s' "
"not allowed here", Template::get_restriction_name(fp_tr));
break;
case TR_NONE:
fp->error("Formal parameter without template restriction "
"not allowed here");
break;
default:
FATAL_ERROR("Ttcn::Def_Template::chk()");
}
break;
default:
FATAL_ERROR("Ttcn::Def_Template::chk()");
}
}
}
}
if (!semantic_check_only) {
if (fp_list) fp_list->set_genname(t_genname);
body->set_genname_prefix("template_");
body->set_genname_recursive(t_genname);
body->set_code_section(fp_list ? GovernedSimple::CS_INLINE :
(my_scope->is_class_scope() ?
GovernedSimple::CS_INIT_CLASS : GovernedSimple::CS_POST_INIT));
if (my_scope->is_class_scope()) {
body->chk_class_member(my_scope->get_scope_class());
}
}
}
}
void Def_Template::chk_default() const
{
if (!fp_list) FATAL_ERROR("Def_Template::chk_default()");
if (!derived_ref) {
if (fp_list->has_notused_defval())
fp_list->error("Only modified templates are allowed to use the not "
"used symbol (`-') as the default parameter");
return;
}
Common::Assignment *ass = derived_ref->get_refd_assignment(false);
if (!ass || ass->get_asstype() != A_TEMPLATE) return; // Work locally.
Def_Template *base = dynamic_cast<Def_Template *>(ass);
if (!base) FATAL_ERROR("Def_Template::chk_default()");
FormalParList *base_fpl = base->get_FormalParList();
size_t nof_base_fps = base_fpl ? base_fpl->get_nof_fps() : 0;
size_t nof_local_fps = fp_list ? fp_list->get_nof_fps() : 0;
size_t min_fps = nof_base_fps;
if (nof_local_fps < nof_base_fps) min_fps = nof_local_fps;
for (size_t i = 0; i < min_fps; i++) {
FormalPar *local_fp = fp_list->get_fp_byIndex(i);
if (local_fp->has_notused_defval()) {
FormalPar *base_fp = base_fpl->get_fp_byIndex(i);
if (base_fp->has_defval_checked()) {
local_fp->set_defval(base_fp->get_defval());
} else {
local_fp->error("Not used symbol (`-') doesn't have the "
"corresponding default parameter in the "
"base template");
}
}
}
// Additional parameters in the derived template with using the not used
// symbol. TODO: Merge the loops.
for (size_t i = nof_base_fps; i < nof_local_fps; i++) {
FormalPar *local_fp = fp_list->get_fp_byIndex(i);
if (local_fp->has_notused_defval())
local_fp->error("Not used symbol (`-') doesn't have the "
"corresponding default parameter in the "
"base template");
}
}
void Def_Template::chk_modified()
{
if (!derived_ref) return;
// Do not check the (non-existent) actual parameter list of the derived
// reference against the formal parameter list of the base template.
// According to TTCN-3 syntax the derived reference cannot have parameters
// even if the base template is parameterized.
Common::Assignment *ass = derived_ref->get_refd_assignment(false);
// Checking the existence and type compatibility of the base template.
if (!ass) return;
if (ass->get_asstype() != A_TEMPLATE) {
derived_ref->error("Reference to a template was expected in the "
"`modifies' definition instead of %s",
ass->get_description().c_str());
return;
}
base_template = dynamic_cast<Def_Template*>(ass);
if (base_template == NULL || body == NULL) {
FATAL_ERROR("Def_Template::chk_modified()");
}
Type *base_type = base_template->get_Type();
TypeCompatInfo info_base(my_scope->get_scope_mod(), type, base_type, true,
false, true);
TypeChain l_chain_base;
TypeChain r_chain_base;
if (!type->is_compatible(base_type, &info_base, this, &l_chain_base,
&r_chain_base)) {
if (info_base.is_subtype_error()) {
type->error("%s", info_base.get_subtype_error().c_str());
} else
if (!info_base.is_erroneous()) {
type->error("The modified template has different type than base "
"template `%s': `%s' was expected instead of `%s'",
ass->get_fullname().c_str(),
base_type->get_typename().c_str(),
type->get_typename().c_str());
} else {
// Always use the format string.
type->error("%s", info_base.get_error_str_str().c_str());
}
} else {
if (info_base.needs_conversion())
body->set_needs_conversion();
}
// Checking formal parameter lists.
FormalParList *base_fpl = base_template->get_FormalParList();
size_t nof_base_fps = base_fpl ? base_fpl->get_nof_fps() : 0;
size_t nof_local_fps = fp_list ? fp_list->get_nof_fps() : 0;
size_t min_fps;
if (nof_local_fps < nof_base_fps) {
error("The modified template has fewer formal parameters than base "
"template `%s': at least %lu parameter%s expected instead of %lu",
ass->get_fullname().c_str(), static_cast<unsigned long>(nof_base_fps),
nof_base_fps > 1 ? "s were" : " was", static_cast<unsigned long>(nof_local_fps));
min_fps = nof_local_fps;
} else min_fps = nof_base_fps;
for (size_t i = 0; i < min_fps; i++) {
FormalPar *base_fp = base_fpl->get_fp_byIndex(i);
FormalPar *local_fp = fp_list->get_fp_byIndex(i);
Error_Context cntxt(local_fp, "In formal parameter #%lu",
static_cast<unsigned long>(i + 1));
// Check for parameter kind equivalence (value or template).
if (base_fp->get_asstype() != local_fp->get_asstype())
local_fp->error("The kind of parameter is not the same as in base "
"template `%s': %s was expected instead of %s",
ass->get_fullname().c_str(), base_fp->get_assname(),
local_fp->get_assname());
// Check for type compatibility.
Type *base_fp_type = base_fp->get_Type();
Type *local_fp_type = local_fp->get_Type();
TypeCompatInfo info_par(my_scope->get_scope_mod(), base_fp_type,
local_fp_type, true, false);
TypeChain l_chain_par;
TypeChain r_chain_par;
if (!base_fp_type->is_compatible(local_fp_type, &info_par, this,
&l_chain_par, &r_chain_par)) {
if (info_par.is_subtype_error()) {
local_fp_type->error("%s", info_par.get_subtype_error().c_str());
} else
if (!info_par.is_erroneous()) {
local_fp_type->error("The type of parameter is not the same as in "
"base template `%s': `%s' was expected instead "
"of `%s'",
ass->get_fullname().c_str(),
base_fp_type->get_typename().c_str(),
local_fp_type->get_typename().c_str());
} else {
local_fp_type->error("%s", info_par.get_error_str_str().c_str());
}
} else {
if (info_par.needs_conversion())
body->set_needs_conversion();
}
// Check for name equivalence.
const Identifier& base_fp_id = base_fp->get_id();
const Identifier& local_fp_id = local_fp->get_id();
if (!(base_fp_id == local_fp_id))
local_fp->error("The name of parameter is not the same as in base "
"template `%s': `%s' was expected instead of `%s'",
ass->get_fullname().c_str(),
base_fp_id.get_dispname().c_str(),
local_fp_id.get_dispname().c_str());
// Check for restrictions: the derived must be same or more restrictive.
if (base_fp->get_asstype()==local_fp->get_asstype() &&
Template::is_less_restrictive(base_fp->get_template_restriction(),
local_fp->get_template_restriction())) {
local_fp->error("The restriction of parameter is not the same or more "
"restrictive as in base template `%s'", ass->get_fullname().c_str());
}
}
// Set the pointer to the body of base template.
body->set_base_template(base_template->get_Template());
}
void Def_Template::chk_recursive_derivation()
{
if (recurs_deriv_checked) return;
if (base_template) {
ReferenceChain refch(this, "While checking the chain of base templates");
refch.add(get_fullname());
for (Def_Template *iter = base_template; iter; iter = iter->base_template)
{
if (iter->recurs_deriv_checked) break;
else if (refch.add(iter->get_fullname()))
iter->recurs_deriv_checked = true;
else break;
}
}
recurs_deriv_checked = true;
}
void Def_Template::generate_code(output_struct *target, bool)
{
type->generate_code(target);
if (body != NULL && body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) {
target->functions.post_init = body->get_err_descr()->generate_code_init_str(
NULL, target->functions.post_init, body->get_lhs_name());
}
if (fp_list) {
// Parameterized template. Generate code for a function which returns
// a $(genname)_template and has the appropriate parameters.
const string& t_genname = get_genname();
const char *template_name = t_genname.c_str();
const char *template_dispname = id->get_dispname().c_str();
const string& type_genname = type->get_genname_template(my_scope);
const char *type_genname_str = type_genname.c_str();
// assemble the function body first (this also determines which parameters
// are never used)
size_t nof_base_pars = 0;
char* function_body = create_location_object(memptystr(), "TEMPLATE",
template_dispname);
if (debugger_active) {
function_body = generate_code_debugger_function_init(function_body, this);
}
if (base_template) {
// modified template
function_body = mputprintf(function_body, "%s ret_val(%s",
type_genname_str,
base_template->get_genname_from_scope(my_scope).c_str());
if (base_template->fp_list) {
// the base template is also parameterized
function_body = mputc(function_body, '(');
nof_base_pars = base_template->fp_list->get_nof_fps();
for (size_t i = 0; i < nof_base_pars; i++) {
if (i > 0) function_body = mputstr(function_body, ", ");
function_body = mputstr(function_body,
fp_list->get_fp_byIndex(i)->get_id().get_name().c_str());
}
function_body = mputc(function_body, ')');
}
function_body = mputstr(function_body, ");\n");
} else {
// simple template
function_body = mputprintf(function_body, "%s ret_val;\n",
type_genname_str);
}
function_body = body->generate_code_init(function_body, "ret_val");
if (template_restriction!=TR_NONE && gen_restriction_check)
function_body = Template::generate_restriction_check_code(function_body,
"ret_val", template_restriction);
if (debugger_active) {
function_body = mputstr(function_body,
"ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
"ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
}
if (ErroneousDescriptors::can_have_err_attribs(type)) {
// these are always generated, not just if the template has erroneous
// descriptors, so adding '@update' statements in other modules does not
// require this module's code to be regenerated
target->source.global_vars = mputprintf(target->source.global_vars,
"Erroneous_descriptor_t* %s_err_descr_ptr = NULL;\n",
body->get_lhs_name().c_str());
target->header.global_vars = mputprintf(target->header.global_vars,
"extern Erroneous_descriptor_t* %s_err_descr_ptr;\n",
body->get_lhs_name().c_str());
function_body = mputprintf(function_body,
"ret_val.set_err_descr(%s_err_descr_ptr);\n",
body->get_lhs_name().c_str());
}
if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) {
target->source.global_vars = body->get_err_descr()->generate_code_str(NULL,
target->source.global_vars, target->header.global_vars, body->get_lhs_name());
target->functions.post_init = mputprintf(target->functions.post_init,
"%s_err_descr_ptr = &%s_%lu_err_descr;\n",
body->get_lhs_name().c_str(), body->get_lhs_name().c_str(),
static_cast<unsigned long>( body->get_err_descr()->get_descr_index(NULL) ));
}
function_body = mputstr(function_body, "return ret_val;\n");
// if the template modifies a parameterized template, then the inherited
// formal parameters must always be displayed, otherwise generate a smart
// formal parameter list (where the names of unused parameters are omitted)
char *formal_par_list = fp_list->generate_code(memptystr(), nof_base_pars);
fp_list->generate_code_defval(target);
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern %s %s(%s);\n",
type_genname_str, template_name, formal_par_list);
target->source.function_bodies = mputprintf(target->source.function_bodies,
"%s %s(%s)\n"
"{\n"
"%s"
"}\n\n", type_genname_str, template_name, formal_par_list, function_body);
Free(formal_par_list);
Free(function_body);
} else {
// non-parameterized template
const_def cdef;
Code::init_cdef(&cdef);
bool in_class = my_scope->is_class_scope();
if (body != NULL) {
type->generate_code_object(&cdef, body, in_class);
cdef.init = update_location_object(cdef.init);
if (base_template) {
// modified template
if (base_template->my_scope->get_scope_mod_gen() ==
my_scope->get_scope_mod_gen()) {
// if the base template is in the same module its body has to be
// initialized first
cdef.init = base_template->body->generate_code_init(cdef.init,
base_template->body->get_lhs_name().c_str());
}
if (use_runtime_2 && body->get_needs_conversion()) {
Type *body_type = body->get_my_governor()->get_type_refd_last();
Type *base_type = base_template->body->get_my_governor()
->get_type_refd_last();
if (!body_type || !base_type)
FATAL_ERROR("Def_Template::generate_code()");
const string& tmp_id = body->get_temporary_id();
const char *tmp_id_str = tmp_id.c_str();
// base template initialization
cdef.init = mputprintf(cdef.init,
"%s %s;\n"
"if (!%s(%s, %s)) TTCN_error(\"Values or templates of types `%s' "
"and `%s' are not compatible at run-time\");\n"
"%s = %s;\n",
body_type->get_genname_template(my_scope).c_str(), tmp_id_str,
TypeConv::get_conv_func(base_type, body_type, my_scope
->get_scope_mod()).c_str(), tmp_id_str, base_template
->get_genname_from_scope(my_scope).c_str(), base_type
->get_typename().c_str(), body_type->get_typename().c_str(),
body->get_lhs_name().c_str(), tmp_id_str);
} else {
cdef.init = mputprintf(cdef.init, "%s = %s;\n",
body->get_lhs_name().c_str(),
base_template->get_genname_from_scope(my_scope).c_str());
}
}
if (use_runtime_2 && TypeConv::needs_conv_refd(body))
cdef.init = TypeConv::gen_conv_code_refd(cdef.init,
body->get_lhs_name().c_str(), body);
else
cdef.init = body->generate_code_init(cdef.init,
body->get_lhs_name().c_str());
if (template_restriction != TR_NONE && gen_restriction_check)
cdef.init = Template::generate_restriction_check_code(cdef.init,
body->get_lhs_name().c_str(), template_restriction);
if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) {
cdef.init = mputprintf(cdef.init, "%s.set_err_descr(&%s_%lu_err_descr);\n",
body->get_lhs_name().c_str(), body->get_lhs_name().c_str(),
static_cast<unsigned long>( body->get_err_descr()->get_descr_index(NULL) ));
}
}
else { // no template body
type->generate_code_object(&cdef, my_scope, get_genname(), "template_", true, false, true);
}
char*& header = in_class ? target->header.class_defs : target->header.global_vars;
header = mputstr(header, cdef.decl);
char*& source = in_class ? target->temp.constructor_init : target->source.global_vars;
source = mputstr(source, cdef.def);
char*& init = in_class ? target->temp.constructor_preamble : target->functions.post_init;
init = mputstr(init, cdef.init);
Code::free_cdef(&cdef);
}
}
void Def_Template::generate_code(Common::CodeGenHelper& cgh) {
generate_code(cgh.get_outputstruct(this));
}
char *Def_Template::generate_code_str(char *str)
{
if (body == NULL) {
FATAL_ERROR("Def_Template::generate_code_str");
}
if (fp_list) {
const char *dispname_str = id->get_dispname().c_str();
NOTSUPP("Code generation for parameterized local template `%s'",
dispname_str);
str = mputprintf(str, "/* NOT SUPPORTED: template %s */\n",
dispname_str);
} else {
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const string& type_genname = type->get_genname_template(my_scope);
const char *type_genname_str = type_genname.c_str();
if (base_template) {
// non-parameterized modified template
if (use_runtime_2 && body->get_needs_conversion()) {
Type *body_type = body->get_my_governor()->get_type_refd_last();
Type *base_type = base_template->body->get_my_governor()
->get_type_refd_last();
if (!body_type || !base_type)
FATAL_ERROR("Def_Template::generate_code_str()");
const string& tmp_id = body->get_temporary_id();
const char *tmp_id_str = tmp_id.c_str();
str = mputprintf(str,
"%s %s;\n"
"if (!%s(%s, %s)) TTCN_error(\"Values or templates of types `%s' "
"and `%s' are not compatible at run-time\");\n"
"%s %s(%s);\n",
body_type->get_genname_template(my_scope).c_str(), tmp_id_str,
TypeConv::get_conv_func(base_type, body_type, my_scope
->get_scope_mod()).c_str(), tmp_id_str, base_template
->get_genname_from_scope(my_scope).c_str(), base_type
->get_typename().c_str(), body_type->get_typename().c_str(),
type_genname_str, genname_str, tmp_id_str);
} else {
// the object is initialized from the base template by the
// constructor
str = mputprintf(str, "%s %s(%s);\n", type_genname_str, genname_str,
base_template->get_genname_from_scope(my_scope).c_str());
}
// the modified body is assigned in the subsequent statements
str = body->generate_code_init(str, genname_str);
} else {
// non-parameterized non-modified template
if (body->has_single_expr()) {
// the object is initialized by the constructor
str = mputprintf(str, "%s %s(%s);\n", type_genname_str,
genname_str, body->get_single_expr(false).c_str());
// make sure the template's code is not generated twice (TR: HU56425)
body->set_code_generated();
} else {
// the default constructor is used
str = mputprintf(str, "%s %s;\n", type_genname_str, genname_str);
// the body is assigned in the subsequent statements
str = body->generate_code_init(str, genname_str);
}
}
if (template_restriction != TR_NONE && gen_restriction_check)
str = Template::generate_restriction_check_code(str, genname_str,
template_restriction);
}
if (debugger_active) {
str = generate_code_debugger_add_var(str, this);
}
return str;
}
void Def_Template::ilt_generate_code(ILT *ilt)
{
if (body == NULL) {
FATAL_ERROR("Def_Template::ilt_generate_code");
}
char*& def=ilt->get_out_def();
char*& init=ilt->get_out_branches();
if (fp_list) {
const char *dispname_str = id->get_dispname().c_str();
NOTSUPP("Code generation for parameterized local template `%s'",
dispname_str);
def = mputprintf(def, "/* NOT SUPPORTED: template %s */\n", dispname_str);
init = mputprintf(init, "/* NOT SUPPORTED: template %s */\n",
dispname_str);
} else {
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
// non-parameterized template
// use the default constructor for initialization
def = mputprintf(def, "%s %s;\n",
type->get_genname_template(my_scope).c_str(), genname_str);
if (base_template) {
// copy the base template with an assignment
init = mputprintf(init, "%s = %s;\n", genname_str,
base_template->get_genname_from_scope(my_scope).c_str());
}
// finally assign the body
init = body->generate_code_init(init, genname_str);
if (template_restriction!=TR_NONE && gen_restriction_check)
init = Template::generate_restriction_check_code(init, genname_str,
template_restriction);
}
}
void Def_Template::dump_internal(unsigned level) const
{
DEBUG(level, "Template: %s", id->get_dispname().c_str());
if (fp_list) fp_list->dump(level + 1);
if (derived_ref)
DEBUG(level + 1, "modifies: %s", derived_ref->get_dispname().c_str());
if (template_restriction!=TR_NONE)
DEBUG(level + 1, "restriction: %s",
Template::get_restriction_name(template_restriction));
type->dump(level + 1);
if (body != NULL) {
body->dump(level + 1);
}
}
// =================================
// ===== Def_Var
// =================================
Def_Var::Def_Var(Identifier *p_id, Type *p_type, Value *p_initial_value)
: Definition(A_VAR, p_id), type(p_type), initial_value(p_initial_value)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_Var::Def_Var()");
type->set_ownertype(Type::OT_VAR_DEF, this);
}
Def_Var::~Def_Var()
{
delete type;
delete initial_value;
}
Def_Var *Def_Var::clone() const
{
FATAL_ERROR("Def_Var::clone");
}
void Def_Var::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
if (initial_value)
initial_value->set_fullname(p_fullname + ".<initial_value>");
}
void Def_Var::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type->set_my_scope(p_scope);
if (initial_value) initial_value->set_my_scope(p_scope);
}
Type *Def_Var::get_Type()
{
chk();
return type;
}
Value* Def_Var::get_Value()
{
chk();
return initial_value;
}
Value* Def_Var::steal_Value()
{
if (!checked) {
FATAL_ERROR("Def_Var::steal_Value");
}
Value* ret_val = initial_value;
initial_value = NULL;
return ret_val;
}
void Def_Var::chk()
{
if(checked) return;
Error_Context cntxt(this, "In %s definition `%s'",
asstype == A_EXCEPTION ? "exception" : "variable", id->get_dispname().c_str());
type->set_genname(_T_, get_genname());
type->chk();
checked = true;
Type *t = type->get_type_refd_last();
switch (t->get_typetype()) {
case Type::T_PORT:
error("%s cannot be defined for port type `%s'",
asstype == A_EXCEPTION ? "Exception" : "Variable", t->get_fullname().c_str());
break;
case Type::T_SIGNATURE:
error("%s cannot be defined for signature `%s'",
asstype == A_EXCEPTION ? "Exception" : "Variable", t->get_fullname().c_str());
break;
case Type::T_DEFAULT:
if (asstype == A_EXCEPTION) {
error("Exception cannot be defined for the default type");
break;
}
// else fall through
default:
if (initial_value) {
initial_value->set_my_governor(type);
type->chk_this_value_ref(initial_value);
namedbool class_member_init = my_scope->is_class_scope() ?
CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT;
type->chk_this_value(initial_value, this, is_local() ?
Type::EXPECTED_DYNAMIC_VALUE : Type::EXPECTED_STATIC_VALUE,
INCOMPLETE_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT,
NOT_STR_ELEM, class_member_init);
if (!semantic_check_only) {
initial_value->set_genname_recursive(get_genname());
initial_value->set_code_section(my_scope->is_class_scope() ?
GovernedSimple::CS_INIT_CLASS : GovernedSimple::CS_INLINE);
if (my_scope->is_class_scope()) {
initial_value->chk_class_member(my_scope->get_scope_class());
}
}
}
break;
}
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
bool Def_Var::chk_identical(Definition *p_def)
{
chk();
p_def->chk();
if (p_def->get_asstype() != A_VAR) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a variable, but the definition "
"inherited from component type `%s' is a %s", dispname_str,
p_def->get_my_scope()->get_fullname().c_str(), p_def->get_assname());
p_def->note("The inherited definition of `%s' is here", dispname_str);
return false;
}
Def_Var *p_def_var = dynamic_cast<Def_Var*>(p_def);
if (!p_def_var) FATAL_ERROR("Def_Var::chk_identical()");
if (!type->is_identical(p_def_var->type)) {
const char *dispname_str = id->get_dispname().c_str();
type->error("Local variable `%s' has type `%s', but the variable "
"inherited from component type `%s' has type `%s'", dispname_str,
type->get_typename().c_str(),
p_def_var->get_my_scope()->get_fullname().c_str(),
p_def_var->type->get_typename().c_str());
p_def_var->note("The inherited variable `%s' is here", dispname_str);
return false;
}
if (initial_value) {
if (p_def_var->initial_value) {
if (!initial_value->is_unfoldable() &&
!p_def_var->initial_value->is_unfoldable() &&
!(*initial_value == *p_def_var->initial_value)) {
const char *dispname_str = id->get_dispname().c_str();
initial_value->warning("Local variable `%s' and the variable "
"inherited from component type `%s' have different initial values",
dispname_str, p_def_var->get_my_scope()->get_fullname().c_str());
p_def_var->note("The inherited variable `%s' is here", dispname_str);
}
} else {
const char *dispname_str = id->get_dispname().c_str();
initial_value->warning("Local variable `%s' has initial value, but "
"the variable inherited from component type `%s' does not",
dispname_str, p_def_var->get_my_scope()->get_fullname().c_str());
p_def_var->note("The inherited variable `%s' is here", dispname_str);
}
} else if (p_def_var->initial_value) {
const char *dispname_str = id->get_dispname().c_str();
warning("Local variable `%s' does not have initial value, but the "
"variable inherited from component type `%s' has", dispname_str,
p_def_var->get_my_scope()->get_fullname().c_str());
p_def_var->note("The inherited variable `%s' is here", dispname_str);
}
return true;
}
void Def_Var::generate_code(output_struct *target, bool clean_up)
{
type->generate_code(target);
const_def cdef;
Code::init_cdef(&cdef);
bool in_class = my_scope->is_class_scope();
type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false,
in_class);
Code::merge_cdef(target, &cdef, in_class);
Code::free_cdef(&cdef);
if (initial_value) {
char*& init = in_class ? target->temp.constructor_preamble :
target->functions.init_comp;
string lhs = initial_value->get_lhs_name();
if (in_class) {
lhs = string("this->") + lhs;
}
init = initial_value->generate_code_init(init, lhs.c_str());
} else if (clean_up) { // No initial value.
target->functions.init_comp = mputprintf(target->functions.init_comp,
"%s.clean_up();\n", get_genname().c_str());
}
}
void Def_Var::generate_code(CodeGenHelper& cgh)
{
generate_code(cgh.get_outputstruct(this));
}
char *Def_Var::generate_code_str(char *str)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
string type_name = type->get_genname_value(my_scope);
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
str = mputprintf(str, "%s %s(%s);\n",
type_name.c_str(), genname_str, initial_value->get_single_expr().c_str());
} else {
// use the default constructor
str = mputprintf(str, "%s %s;\n",
type_name.c_str(), genname_str);
if (initial_value) {
// the initial value is assigned using subsequent statements
str = initial_value->generate_code_init(str, genname_str);
}
}
if (debugger_active) {
str = generate_code_debugger_add_var(str, this);
}
return str;
}
void Def_Var::ilt_generate_code(ILT *ilt)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
char*& def=ilt->get_out_def();
char*& init=ilt->get_out_branches();
def = mputprintf(def, "%s %s;\n", type->get_genname_value(my_scope).c_str(),
genname_str);
if (initial_value)
init = initial_value->generate_code_init(init, genname_str);
}
char *Def_Var::generate_code_init_comp(char *str, Definition *base_defn)
{
if (initial_value) {
str = initial_value->generate_code_init(str,
base_defn->get_genname_from_scope(my_scope).c_str());
}
return str;
}
void Def_Var::dump_internal(unsigned level) const
{
DEBUG(level, "Variable %s", id->get_dispname().c_str());
type->dump(level + 1);
if (initial_value) initial_value->dump(level + 1);
}
// =================================
// ===== Def_Exception
// =================================
Def_Exception::Def_Exception(Identifier* p_id, Type* p_type): Def_Var(p_id, p_type, NULL), usage_found(false)
{
asstype = Common::Assignment::A_EXCEPTION;
}
char* Def_Exception::generate_code_str(char *str)
{
if (!usage_found) {
return str;
}
Common::Type* type_last = type->get_type_refd_last();
if (type_last->get_typetype() == Common::Type::T_CLASS) {
ClassTypeBody* class_ = type_last->get_class_type_body();
string class_name = class_->is_built_in() ? string("OBJECT") : type_last->get_genname_own(my_scope);
return mputprintf(str, "CLASS_EXCEPTION<%s> %s(exc_base.get_object_ref().cast_to_dyn<%s>());\n",
class_name.c_str(), id->get_name().c_str(), class_name.c_str());
}
return mputprintf(str, "NON_CLASS_EXCEPTION<%s>& %s = static_cast<NON_CLASS_EXCEPTION<%s>&>(exc_base);\n",
type->get_genname_value(my_scope).c_str(), id->get_name().c_str(), type->get_genname_value(my_scope).c_str());
}
// =================================
// ===== Def_Var_Template
// =================================
Def_Var_Template::Def_Var_Template(Identifier *p_id, Type *p_type,
Template *p_initial_value, template_restriction_t p_template_restriction)
: Definition(A_VAR_TEMPLATE, p_id), type(p_type),
initial_value(p_initial_value), template_restriction(p_template_restriction)
{
if (!p_type) FATAL_ERROR("Ttcn::Def_Var_Template::Def_Var_Template()");
type->set_ownertype(Type::OT_VARTMPL_DEF, this);
}
Def_Var_Template::~Def_Var_Template()
{
delete type;
delete initial_value;
}
Def_Var_Template *Def_Var_Template::clone() const
{
FATAL_ERROR("Def_Var_Template::clone");
}
void Def_Var_Template::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type->set_fullname(p_fullname + ".<type>");
if (initial_value)
initial_value->set_fullname(p_fullname + ".<initial_value>");
}
void Def_Var_Template::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type->set_my_scope(p_scope);
if (initial_value) initial_value->set_my_scope(p_scope);
}
Type *Def_Var_Template::get_Type()
{
chk();
return type;
}
Template* Def_Var_Template::get_Template()
{
chk();
return initial_value;
}
Template* Def_Var_Template::steal_Template()
{
if (!checked) {
FATAL_ERROR("Def_Var_Template::steal_Template");
}
Template* ret_val = initial_value;
initial_value = NULL;
return ret_val;
}
void Def_Var_Template::chk()
{
if(checked) return;
Error_Context cntxt(this, "In template variable definition `%s'",
id->get_dispname().c_str());
type->set_genname(_T_, get_genname());
type->chk();
checked = true;
Type *t = type->get_type_refd_last();
if (t->get_typetype() == Type::T_PORT) {
error("Template variable cannot be defined for port type `%s'",
t->get_fullname().c_str());
}
else if (t->get_typetype() == Type::T_CLASS) {
error("Template variable cannot be defined for class type `%s'",
t->get_typename().c_str());
}
if (initial_value) {
initial_value->set_my_governor(type);
initial_value->flatten(false);
if (initial_value->get_templatetype() == Template::CSTR_PATTERN &&
type->get_type_refd_last()->get_typetype() == Type::T_USTR) {
initial_value->set_templatetype(Template::USTR_PATTERN);
initial_value->get_ustr_pattern()->set_pattern_type(
PatternString::USTR_PATTERN);
}
type->chk_this_template_ref(initial_value);
namedbool class_member_init = my_scope->is_class_scope() ?
CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT;
type->chk_this_template_generic(initial_value, INCOMPLETE_ALLOWED,
OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK, IMPLICIT_OMIT, class_member_init, 0);
gen_restriction_check =
initial_value->chk_restriction("template variable definition",
template_restriction, initial_value);
type->chk_this_template_incorrect_field();
if (!semantic_check_only) {
initial_value->set_genname_recursive(get_genname());
initial_value->set_code_section(my_scope->is_class_scope() ?
GovernedSimple::CS_INIT_CLASS : GovernedSimple::CS_INLINE);
if (my_scope->is_class_scope()) {
initial_value->chk_class_member(my_scope->get_scope_class());
}
}
}
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
bool Def_Var_Template::chk_identical(Definition *p_def)
{
chk();
p_def->chk();
if (p_def->get_asstype() != A_VAR_TEMPLATE) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a template variable, but the definition "
"inherited from component type `%s' is a %s", dispname_str,
p_def->get_my_scope()->get_fullname().c_str(), p_def->get_assname());
p_def->note("The inherited definition of `%s' is here", dispname_str);
return false;
}
Def_Var_Template *p_def_var_template =
dynamic_cast<Def_Var_Template*>(p_def);
if (!p_def_var_template) FATAL_ERROR("Def_Var_Template::chk_identical()");
if (!type->is_identical(p_def_var_template->type)) {
const char *dispname_str = id->get_dispname().c_str();
type->error("Local template variable `%s' has type `%s', but the "
"template variable inherited from component type `%s' has type `%s'",
dispname_str, type->get_typename().c_str(),
p_def_var_template->get_my_scope()->get_fullname().c_str(),
p_def_var_template->type->get_typename().c_str());
p_def_var_template->note("The inherited template variable `%s' is here",
dispname_str);
return false;
}
if (initial_value) {
if (!p_def_var_template->initial_value) {
const char *dispname_str = id->get_dispname().c_str();
initial_value->warning("Local template variable `%s' has initial "
"value, but the template variable inherited from component type "
"`%s' does not", dispname_str,
p_def_var_template->get_my_scope()->get_fullname().c_str());
p_def_var_template->note("The inherited template variable `%s' is here",
dispname_str);
}
} else if (p_def_var_template->initial_value) {
const char *dispname_str = id->get_dispname().c_str();
warning("Local template variable `%s' does not have initial value, but "
"the template variable inherited from component type `%s' has",
dispname_str,
p_def_var_template->get_my_scope()->get_fullname().c_str());
p_def_var_template->note("The inherited template variable `%s' is here",
dispname_str);
}
return true;
}
void Def_Var_Template::generate_code(output_struct *target, bool clean_up)
{
type->generate_code(target);
const_def cdef;
Code::init_cdef(&cdef);
bool in_class = my_scope->is_class_scope();
type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false,
in_class);
Code::merge_cdef(target, &cdef, in_class);
Code::free_cdef(&cdef);
if (initial_value) {
char*& init = in_class ? target->temp.constructor_preamble :
target->functions.init_comp;
string lhs = initial_value->get_lhs_name();
if (in_class) {
lhs = string("this->") + lhs;
}
if (Common::Type::T_SEQOF == initial_value->get_my_governor()->get_typetype() ||
Common::Type::T_ARRAY == initial_value->get_my_governor()->get_typetype()) {
init = mputprintf(init, "%s.remove_all_permutations();\n", lhs.c_str());
}
init = initial_value->generate_code_init(init, lhs.c_str());
if (template_restriction!=TR_NONE && gen_restriction_check)
init = Template::generate_restriction_check_code(init,
lhs.c_str(), template_restriction);
} else if (clean_up) { // No initial value.
// Always reset component variables/variable templates on component
// reinitialization. Fix for HM79493.
target->functions.init_comp = mputprintf(target->functions.init_comp,
"%s.clean_up();\n", get_genname().c_str());
}
}
void Def_Var_Template::generate_code(CodeGenHelper& cgh)
{
generate_code(cgh.get_outputstruct(this));
}
char *Def_Var_Template::generate_code_str(char *str)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
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.
str = mputprintf(str, "%s %s(%s);\n",
type->get_genname_template(my_scope).c_str(), genname_str,
initial_value->get_single_expr(false).c_str());
} else {
// Use the default constructor.
str = mputprintf(str, "%s %s;\n",
type->get_genname_template(my_scope).c_str(), genname_str);
if (initial_value) {
// The initial value is assigned using subsequent statements.
if (use_runtime_2 && TypeConv::needs_conv_refd(initial_value))
str = TypeConv::gen_conv_code_refd(str, genname_str, initial_value);
else str = initial_value->generate_code_init(str, genname_str);
}
}
if (initial_value && template_restriction != TR_NONE
&& gen_restriction_check)
str = Template::generate_restriction_check_code(str, genname_str,
template_restriction);
if (debugger_active) {
str = generate_code_debugger_add_var(str, this);
}
return str;
}
void Def_Var_Template::ilt_generate_code(ILT *ilt)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
char*& def=ilt->get_out_def();
def = mputprintf(def, "%s %s;\n",
type->get_genname_template(my_scope).c_str(), genname_str);
if (initial_value) {
char*& init=ilt->get_out_branches();
init = initial_value->generate_code_init(init, genname_str);
if (template_restriction!=TR_NONE && gen_restriction_check)
init = Template::generate_restriction_check_code(init, genname_str,
template_restriction);
}
}
char *Def_Var_Template::generate_code_init_comp(char *str,
Definition *base_defn)
{
if (initial_value) {
str = initial_value->generate_code_init(str,
base_defn->get_genname_from_scope(my_scope).c_str());
if (template_restriction != TR_NONE && gen_restriction_check)
str = Template::generate_restriction_check_code(str,
base_defn->get_genname_from_scope(my_scope).c_str(),
template_restriction);
}
return str;
}
void Def_Var_Template::dump_internal(unsigned level) const
{
DEBUG(level, "Template variable %s", id->get_dispname().c_str());
if (template_restriction!=TR_NONE)
DEBUG(level + 1, "restriction: %s",
Template::get_restriction_name(template_restriction));
type->dump(level + 1);
if (initial_value) initial_value->dump(level + 1);
}
// =================================
// ===== Def_Timer
// =================================
Def_Timer::~Def_Timer()
{
delete dimensions;
delete default_duration;
}
Def_Timer *Def_Timer::clone() const
{
FATAL_ERROR("Def_Timer::clone");
}
void Def_Timer::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
if (dimensions) dimensions->set_fullname(p_fullname + ".<dimensions>");
if (default_duration)
default_duration->set_fullname(p_fullname + ".<default_duration>");
}
void Def_Timer::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
if (dimensions) dimensions->set_my_scope(p_scope);
if (default_duration) default_duration->set_my_scope(p_scope);
}
ArrayDimensions *Def_Timer::get_Dimensions()
{
if (!checked) chk();
return dimensions;
}
void Def_Timer::chk()
{
if(checked) return;
Error_Context cntxt(this, "In timer definition `%s'",
id->get_dispname().c_str());
if (dimensions) dimensions->chk();
if (default_duration) {
Error_Context cntxt2(default_duration, "In default duration");
if (dimensions) chk_array_duration(default_duration);
else chk_single_duration(default_duration);
if (!semantic_check_only) {
default_duration->set_code_section(GovernedSimple::CS_POST_INIT);
}
if (my_scope->is_class_scope()) {
default_duration->chk_class_member(my_scope->get_scope_class());
}
}
checked = true;
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
bool Def_Timer::chk_identical(Definition *p_def)
{
chk();
p_def->chk();
if (p_def->get_asstype() != A_TIMER) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a timer, but the definition inherited "
"from component type `%s' is a %s", dispname_str,
p_def->get_my_scope()->get_fullname().c_str(), p_def->get_assname());
p_def->note("The inherited definition of `%s' is here", dispname_str);
return false;
}
Def_Timer *p_def_timer = dynamic_cast<Def_Timer*>(p_def);
if (!p_def_timer) FATAL_ERROR("Def_Timer::chk_identical()");
if (dimensions) {
if (p_def_timer->dimensions) {
if (!dimensions->is_identical(p_def_timer->dimensions)) {
const char *dispname_str = id->get_dispname().c_str();
error("Local timer `%s' and the timer inherited from component type "
"`%s' have different array dimensions", dispname_str,
p_def_timer->get_my_scope()->get_fullname().c_str());
p_def_timer->note("The inherited timer `%s' is here", dispname_str);
return false;
}
} else {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a timer array, but the definition "
"inherited from component type `%s' is a single timer", dispname_str,
p_def_timer->get_my_scope()->get_fullname().c_str());
p_def_timer->note("The inherited timer `%s' is here", dispname_str);
return false;
}
} else if (p_def_timer->dimensions) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a single timer, but the definition "
"inherited from component type `%s' is a timer array", dispname_str,
p_def_timer->get_my_scope()->get_fullname().c_str());
p_def_timer->note("The inherited timer `%s' is here", dispname_str);
return false;
}
if (default_duration) {
if (p_def_timer->default_duration) {
if (!default_duration->is_unfoldable() &&
!p_def_timer->default_duration->is_unfoldable() &&
!(*default_duration == *p_def_timer->default_duration)) {
const char *dispname_str = id->get_dispname().c_str();
default_duration->warning("Local timer `%s' and the timer inherited "
"from component type `%s' have different default durations",
dispname_str, p_def_timer->get_my_scope()->get_fullname().c_str());
p_def_timer->note("The inherited timer `%s' is here", dispname_str);
}
} else {
const char *dispname_str = id->get_dispname().c_str();
default_duration->error("Local timer `%s' has default duration, but "
"the timer inherited from component type `%s' does not", dispname_str,
p_def_timer->get_my_scope()->get_fullname().c_str());
p_def_timer->note("The inherited timer `%s' is here", dispname_str);
return false;
}
} else if (p_def_timer->default_duration) {
const char *dispname_str = id->get_dispname().c_str();
error("Local timer `%s' does not have default duration, but the timer "
"inherited from component type `%s' has", dispname_str,
p_def_timer->get_my_scope()->get_fullname().c_str());
p_def_timer->note("The inherited timer `%s' is here", dispname_str);
return false;
}
return true;
}
bool Def_Timer::has_default_duration(FieldOrArrayRefs *p_subrefs)
{
// return true in case of any uncertainity
if (!default_duration) return false;
else if (!dimensions || !p_subrefs) return true;
Value *v = default_duration;
size_t nof_dims = dimensions->get_nof_dims();
size_t nof_refs = p_subrefs->get_nof_refs();
size_t upper_limit = nof_dims < nof_refs ? nof_dims : nof_refs;
for (size_t i = 0; i < upper_limit; i++) {
v = v->get_value_refd_last();
if (v->get_valuetype() != Value::V_SEQOF) break;
FieldOrArrayRef *ref = p_subrefs->get_ref(i);
if (ref->get_type() != FieldOrArrayRef::ARRAY_REF) return true;
Value *v_index = ref->get_val()->get_value_refd_last();
if (v_index->get_valuetype() != Value::V_INT) return true;
Int index = v_index->get_val_Int()->get_val()
- dimensions->get_dim_byIndex(i)->get_offset();
if (index >= 0 && index < static_cast<Int>(v->get_nof_comps()))
v = v->get_comp_byIndex(index);
else return true;
}
return v->get_valuetype() != Value::V_NOTUSED;
}
void Def_Timer::chk_single_duration(Value *dur)
{
dur->chk_expr_float(is_local() ?
Type::EXPECTED_DYNAMIC_VALUE : Type::EXPECTED_STATIC_VALUE);
Value *v = dur->get_value_refd_last();
if (v->get_valuetype() == Value::V_REAL) {
ttcn3float v_real = v->get_val_Real();
if ( (v_real<0.0) || isSpecialFloatValue(v_real) ) {
dur->error("A non-negative float value was expected "
"as timer duration instead of `%s'", Real2string(v_real).c_str());
}
}
}
void Def_Timer::chk_array_duration(Value *dur, size_t start_dim)
{
ArrayDimension *dim = dimensions->get_dim_byIndex(start_dim);
bool array_size_known = !dim->get_has_error();
size_t array_size = 0;
if (array_size_known) array_size = dim->get_size();
Value *v = dur->get_value_refd_last();
switch (v->get_valuetype()) {
case Value::V_ERROR:
return;
case Value::V_SEQOF: {
size_t nof_vs = v->get_nof_comps();
// Value-list notation.
if (!v->is_indexed()) {
if (array_size_known) {
if (array_size > nof_vs) {
dur->error("Too few elements in the default duration of timer "
"array: %lu was expected instead of %lu",
static_cast<unsigned long>(array_size), static_cast<unsigned long>(nof_vs));
} else if (array_size < nof_vs) {
dur->error("Too many elements in the default duration of timer "
"array: %lu was expected instead of %lu",
static_cast<unsigned long>(array_size), static_cast<unsigned long>(nof_vs));
}
}
bool last_dimension = start_dim + 1 >= dimensions->get_nof_dims();
for (size_t i = 0; i < nof_vs; i++) {
Value *array_v = v->get_comp_byIndex(i);
if (array_v->get_valuetype() == Value::V_NOTUSED) continue;
if (last_dimension) chk_single_duration(array_v);
else chk_array_duration(array_v, start_dim + 1);
}
} else {
// Indexed-notation.
bool last_dimension = start_dim + 1 >= dimensions->get_nof_dims();
map<Int, Int> index_map;
for (size_t i = 0; i < nof_vs; i++) {
Value *array_v = v->get_comp_byIndex(i);
if (array_v->get_valuetype() == Value::V_NOTUSED) continue;
if (last_dimension) chk_single_duration(array_v);
else chk_array_duration(array_v, start_dim + 1);
Error_Context cntxt(this, "In timer array element %lu",
static_cast<unsigned long>(i + 1));
Value *index = v->get_index_byIndex(i);
dim->chk_index(index, Type::EXPECTED_DYNAMIC_VALUE);
if (index->get_value_refd_last()->get_valuetype() == Value::V_INT) {
const int_val_t *index_int = index->get_value_refd_last()
->get_val_Int();
if (*index_int > INT_MAX) {
index->error("An integer value less than `%d' was expected for "
"indexing timer array instead of `%s'", INT_MAX,
(index_int->t_str()).c_str());
index->set_valuetype(Value::V_ERROR);
} else {
Int index_val = index_int->get_val();
if (index_map.has_key(index_val)) {
index->error("Duplicate index value `%s' for timer array "
"elements `%s' and `%s'",
Int2string(index_val).c_str(),
Int2string(static_cast<Int>(i) + 1).c_str(),
Int2string(*index_map[index_val]).c_str());
index->set_valuetype(Value::V_ERROR);
} else {
index_map.add(index_val, new Int(static_cast<Int>(i + 1)));
}
}
}
}
// It's not possible to have "index_map.size() > array_size", since we
// add only correct constant-index values into the map. It's possible
// to create partially initialized timer arrays.
for (size_t i = 0; i < index_map.size(); i++)
delete index_map.get_nth_elem(i);
index_map.clear();
}
break; }
default:
if (array_size_known) {
dur->error("An array value (with %lu elements) was expected as "
"default duration of timer array",
static_cast<unsigned long>(array_size));
} else {
dur->error("An array value was expected as default duration of timer "
"array");
}
dur->set_valuetype(Value::V_ERROR);
return;
}
}
void Def_Timer::generate_code(output_struct *target, bool)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const string& dispname = id->get_dispname();
bool in_class = my_scope->is_class_scope();
char*& header = in_class ? target->header.class_defs :
target->header.global_vars;
char*& source = in_class ? target->temp.constructor_preamble :
target->source.global_vars;
char*& pre_init = in_class ? target->temp.constructor_preamble :
target->functions.pre_init;
char*& post_init = in_class ? target->temp.constructor_preamble :
target->functions.post_init;
if (dimensions) {
// timer array
const string& array_type = dimensions->get_timer_type();
const char *array_type_str = array_type.c_str();
header = mputprintf(header,
"%s%s %s;\n", in_class ? "" : "extern ", array_type_str, genname_str);
if (!in_class) {
source = mputprintf(source, "%s %s;\n", array_type_str, genname_str);
}
pre_init = mputstr(pre_init, "{\n"
"static const char * const timer_name = \"");
pre_init = mputstr(pre_init,
dispname.c_str());
pre_init = mputprintf(pre_init, "\";\n"
"%s.set_name(timer_name);\n"
"}\n", genname_str);
if (default_duration) post_init =
generate_code_array_duration(post_init, genname_str,
default_duration);
} else {
// single timer
header = mputprintf(header, "%sTIMER %s;\n",
in_class ? "" : "extern ", genname_str);
if (default_duration) {
// has default duration
Value *v = default_duration->get_value_refd_last();
if (v->get_valuetype() == Value::V_REAL) {
// duration is known at compilation time -> set in the constructor
if (in_class) {
source = mputprintf(source,
"%s.set_name(\"%s\");\n"
"%s.set_default_duration(%s);\n",
genname_str, dispname.c_str(),
genname_str, v->get_single_expr().c_str());
}
else {
source = mputprintf(source, "TIMER %s(\"%s\", %s);\n",
genname_str, dispname.c_str(), v->get_single_expr().c_str());
}
} else {
// duration is known only at runtime -> set in post_init
if (in_class) {
source = mputprintf(source, "%s.set_name(\"%s\");\n",
genname_str, dispname.c_str());
}
else {
source = mputprintf(source, "TIMER %s(\"%s\");\n",
genname_str, dispname.c_str());
}
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr, "%s.set_default_duration(",
genname_str);
default_duration->generate_code_expr(&expr);
expr.expr = mputc(expr.expr, ')');
post_init = Code::merge_free_expr(post_init, &expr);
}
} else {
// does not have default duration
if (in_class) {
source = mputprintf(source, "%s.set_name(\"%s\");\n",
genname_str, dispname.c_str());
}
else {
source = mputprintf(source, "TIMER %s(\"%s\");\n",
genname_str, dispname.c_str());
}
}
}
}
void Def_Timer::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
char *Def_Timer::generate_code_array_duration(char *str,
const char *object_name, Value *dur, size_t start_dim)
{
ArrayDimension *dim = dimensions->get_dim_byIndex(start_dim);
size_t dim_size = dim->get_size();
Value *v = dur->get_value_refd_last();
if (v->get_valuetype() != Value::V_SEQOF
|| (v->get_nof_comps() != dim_size && !v->is_indexed()))
FATAL_ERROR("Def_Timer::generate_code_array_duration()");
// Value-list notation.
if (!v->is_indexed()) {
if (start_dim + 1 < dimensions->get_nof_dims()) {
// There are more dimensions, the elements of "v" are arrays a
// temporary reference shall be introduced if the next dimension has
// more than 1 elements.
bool temp_ref_needed =
dimensions->get_dim_byIndex(start_dim + 1)->get_size() > 1;
for (size_t i = 0; i < dim_size; i++) {
Value *v_elem = v->get_comp_byIndex(i);
if (v_elem->get_valuetype() == Value::V_NOTUSED) continue;
if (temp_ref_needed) {
const string& tmp_id = my_scope->get_scope_mod_gen()
->get_temporary_id();
const char *tmp_str = tmp_id.c_str();
str = mputprintf(str, "{\n"
"%s& %s = %s.array_element(%lu);\n",
dimensions->get_timer_type(start_dim + 1).c_str(),
tmp_str, object_name, static_cast<unsigned long>(i));
str = generate_code_array_duration(str, tmp_str, v_elem,
start_dim + 1);
str = mputstr(str, "}\n");
} else {
char *tmp_str = mprintf("%s.array_element(%lu)", object_name,
static_cast<unsigned long>(i));
str = generate_code_array_duration(str, tmp_str, v_elem,
start_dim + 1);
Free(tmp_str);
}
}
} else {
// We are in the last dimension, the elements of "v" are floats.
for (size_t i = 0; i < dim_size; i++) {
Value *v_elem = v->get_comp_byIndex(i);
if (v_elem->get_valuetype() == Value::V_NOTUSED) continue;
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr,
"%s.array_element(%lu).set_default_duration(",
object_name, static_cast<unsigned long>(i));
v_elem->generate_code_expr(&expr);
expr.expr = mputc(expr.expr, ')');
str = Code::merge_free_expr(str, &expr);
}
}
// Indexed-list notation.
} else {
if (start_dim + 1 < dimensions->get_nof_dims()) {
bool temp_ref_needed =
dimensions->get_dim_byIndex(start_dim + 1)->get_size() > 1;
for (size_t i = 0; i < v->get_nof_comps(); i++) {
Value *v_elem = v->get_comp_byIndex(i);
if (v_elem->get_valuetype() == Value::V_NOTUSED) continue;
if (temp_ref_needed) {
const string& tmp_id = my_scope->get_scope_mod_gen()
->get_temporary_id();
const string& idx_id = my_scope->get_scope_mod_gen()
->get_temporary_id();
const char *tmp_str = tmp_id.c_str();
str = mputstr(str, "{\n");
str = mputprintf(str, "int %s;\n", idx_id.c_str());
str = v->get_index_byIndex(i)->generate_code_init(str,
idx_id.c_str());
str = mputprintf(str, "%s& %s = %s.array_element(%s);\n",
dimensions->get_timer_type(start_dim + 1).c_str(),
tmp_str, object_name, idx_id.c_str());
str = generate_code_array_duration(str, tmp_str, v_elem,
start_dim + 1);
str = mputstr(str, "}\n");
} else {
const string& idx_id = my_scope->get_scope_mod_gen()
->get_temporary_id();
str = mputstr(str, "{\n");
str = mputprintf(str, "int %s;\n", idx_id.c_str());
str = v->get_index_byIndex(i)->generate_code_init(str,
idx_id.c_str());
char *tmp_str = mprintf("%s.array_element(%s)", object_name,
idx_id.c_str());
str = generate_code_array_duration(str, tmp_str, v_elem,
start_dim + 1);
str = mputstr(str, "}\n");
Free(tmp_str);
}
}
} else {
for (size_t i = 0; i < v->get_nof_comps(); i++) {
Value *v_elem = v->get_comp_byIndex(i);
if (v_elem->get_valuetype() == Value::V_NOTUSED) continue;
expression_struct expr;
Code::init_expr(&expr);
str = mputstr(str, "{\n");
const string& idx_id = my_scope->get_scope_mod_gen()
->get_temporary_id();
str = mputprintf(str, "int %s;\n", idx_id.c_str());
str = v->get_index_byIndex(i)->generate_code_init(str,
idx_id.c_str());
str = mputprintf(str,
"%s.array_element(%s).set_default_duration(",
object_name, idx_id.c_str());
v_elem->generate_code_expr(&expr);
expr.expr = mputc(expr.expr, ')');
str = Code::merge_free_expr(str, &expr);
str = mputstr(str, "}\n");
}
}
}
return str;
}
char *Def_Timer::generate_code_str(char *str)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const string& dispname = id->get_dispname();
if (dimensions) {
// timer array
const string& array_type = dimensions->get_timer_type();
const char *array_type_str = array_type.c_str();
str = mputprintf(str, "%s %s;\n", array_type_str, genname_str);
str = mputstr(str, "{\n"
"static const char * const timer_name = \"");
str = mputstr(str, dispname.c_str());
str = mputprintf(str, "\";\n"
"%s.set_name(timer_name);\n"
"}\n", genname_str);
if (default_duration) str = generate_code_array_duration(str,
genname_str, default_duration);
} else {
// single timer
if (default_duration && default_duration->has_single_expr()) {
// the default duration can be passed to the constructor
str = mputprintf(str, "TIMER %s(\"%s\", %s);\n", genname_str,
dispname.c_str(), default_duration->get_single_expr().c_str());
} else {
// only the name is passed to the constructor
str = mputprintf(str, "TIMER %s(\"%s\");\n", genname_str,
dispname.c_str());
if (default_duration) {
// the default duration is set explicitly
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr, "%s.set_default_duration(",
genname_str);
default_duration->generate_code_expr(&expr);
expr.expr = mputc(expr.expr, ')');
str = Code::merge_free_expr(str, &expr);
}
}
}
if (debugger_active) {
str = generate_code_debugger_add_var(str, this);
}
return str;
}
void Def_Timer::ilt_generate_code(ILT *ilt)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const string& dispname = id->get_dispname();
char*& def = ilt->get_out_def();
char*& init = ilt->get_out_branches();
if (dimensions) {
// timer array
const string& array_type = dimensions->get_timer_type();
const char *array_type_str = array_type.c_str();
def = mputprintf(def, "%s %s;\n", array_type_str, genname_str);
def = mputstr(def, "{\n"
"static const char * const timer_names[] = { ");
def = dimensions->generate_element_names(def, dispname);
def = mputprintf(def, " };\n"
"%s.set_name(%lu, timer_names);\n"
"}\n", genname_str, static_cast<unsigned long>( dimensions->get_array_size() ));
if (default_duration) init = generate_code_array_duration(init,
genname_str, default_duration);
} else {
// single timer
if (default_duration) {
// has default duration
Value *v = default_duration->get_value_refd_last();
if (v->get_valuetype() == Value::V_REAL) {
// duration is known at compilation time -> set in the constructor
def = mputprintf(def, "TIMER %s(\"%s\", %s);\n", genname_str,
dispname.c_str(), v->get_single_expr().c_str());
} else {
// duration is known only at runtime -> set when control reaches the
// timer definition
def = mputprintf(def, "TIMER %s(\"%s\");\n", genname_str,
dispname.c_str());
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr, "%s.set_default_duration(",
genname_str);
default_duration->generate_code_expr(&expr);
expr.expr = mputc(expr.expr, ')');
init = Code::merge_free_expr(init, &expr);
}
} else {
// does not have default duration
def = mputprintf(def, "TIMER %s(\"%s\");\n", genname_str,
dispname.c_str());
}
}
}
char *Def_Timer::generate_code_init_comp(char *str, Definition *base_defn)
{
if (default_duration) {
Def_Timer *base_timer_defn = dynamic_cast<Def_Timer*>(base_defn);
if (!base_timer_defn || !base_timer_defn->default_duration)
FATAL_ERROR("Def_Timer::generate_code_init_comp()");
// initializer is not needed if the default durations are the same
// constants in both timers
if (default_duration->is_unfoldable() ||
base_timer_defn->default_duration->is_unfoldable() ||
!(*default_duration == *base_timer_defn->default_duration)) {
if (dimensions) {
str = generate_code_array_duration(str,
base_timer_defn->get_genname_from_scope(my_scope).c_str(),
default_duration);
} else {
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr, "%s.set_default_duration(",
base_timer_defn->get_genname_from_scope(my_scope).c_str());
default_duration->generate_code_expr(&expr);
expr.expr = mputc(expr.expr, ')');
str = Code::merge_free_expr(str, &expr);
}
}
}
return str;
}
void Def_Timer::dump_internal(unsigned level) const
{
DEBUG(level, "Timer: %s", id->get_dispname().c_str());
if (dimensions) dimensions->dump(level + 1);
if (default_duration) {
DEBUG(level + 1, "Default duration:");
default_duration->dump(level + 1);
}
}
// =================================
// ===== Def_Port
// =================================
Def_Port::Def_Port(Identifier *p_id, Reference *p_tref,
ArrayDimensions *p_dims)
: Definition(A_PORT, p_id), type_ref(p_tref), port_type(0),
dimensions(p_dims)
{
if (!p_tref) FATAL_ERROR("Def_Port::Def_Port()");
}
Def_Port::~Def_Port()
{
delete type_ref;
delete dimensions;
}
Def_Port *Def_Port::clone() const
{
FATAL_ERROR("Def_Port::clone");
}
void Def_Port::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
type_ref->set_fullname(p_fullname + ".<type_ref>");
if (dimensions) dimensions->set_fullname(p_fullname);
}
void Def_Port::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
type_ref->set_my_scope(p_scope);
if (dimensions) dimensions->set_my_scope(p_scope);
}
Type *Def_Port::get_Type()
{
chk();
return port_type;
}
ArrayDimensions *Def_Port::get_Dimensions()
{
if (!checked) chk();
return dimensions;
}
void Def_Port::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In port definition `%s'",
id->get_dispname().c_str());
Common::Assignment *ass = type_ref->get_refd_assignment();
if (ass) {
if (ass->get_asstype() == A_TYPE) {
Type *t = ass->get_Type()->get_type_refd_last();
if (t->get_typetype() == Type::T_PORT) port_type = t;
else type_ref->error("Type reference `%s' does not refer to a "
"port type", type_ref->get_dispname().c_str());
} else type_ref->error("Reference `%s' does not refer to a "
"type", type_ref->get_dispname().c_str());
}
if (dimensions) dimensions->chk();
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
bool Def_Port::chk_identical(Definition *p_def)
{
chk();
p_def->chk();
if (p_def->get_asstype() != A_PORT) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a port, but the definition inherited "
"from component type `%s' is a %s", dispname_str,
p_def->get_my_scope()->get_fullname().c_str(), p_def->get_assname());
p_def->note("The inherited definition of `%s' is here", dispname_str);
return false;
}
Def_Port *p_def_port = dynamic_cast<Def_Port*>(p_def);
if (!p_def_port) FATAL_ERROR("Def_Port::chk_identical()");
if (port_type && p_def_port->port_type &&
port_type != p_def_port->port_type) {
const char *dispname_str = id->get_dispname().c_str();
type_ref->error("Local port `%s' has type `%s', but the port inherited "
"from component type `%s' has type `%s'", dispname_str,
port_type->get_typename().c_str(),
p_def_port->get_my_scope()->get_fullname().c_str(),
p_def_port->port_type->get_typename().c_str());
p_def_port->note("The inherited port `%s' is here", dispname_str);
return false;
}
if (dimensions) {
if (p_def_port->dimensions) {
if (!dimensions->is_identical(p_def_port->dimensions)) {
const char *dispname_str = id->get_dispname().c_str();
error("Local port `%s' and the port inherited from component type "
"`%s' have different array dimensions", dispname_str,
p_def_port->get_my_scope()->get_fullname().c_str());
p_def_port->note("The inherited port `%s' is here", dispname_str);
return false;
}
} else {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a port array, but the definition "
"inherited from component type `%s' is a single port", dispname_str,
p_def_port->get_my_scope()->get_fullname().c_str());
p_def_port->note("The inherited port `%s' is here", dispname_str);
return false;
}
} else if (p_def_port->dimensions) {
const char *dispname_str = id->get_dispname().c_str();
error("Local definition `%s' is a single port, but the definition "
"inherited from component type `%s' is a port array", dispname_str,
p_def_port->get_my_scope()->get_fullname().c_str());
p_def_port->note("The inherited port `%s' is here", dispname_str);
return false;
}
return true;
}
void Def_Port::generate_code(output_struct *target, bool)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const string& type_genname = port_type->get_genname_value(my_scope);
const string& dispname = id->get_dispname();
if (dimensions) {
// port array
const string& array_type = dimensions->get_port_type(type_genname);
const char *array_type_str = array_type.c_str();
target->header.global_vars = mputprintf(target->header.global_vars,
"extern %s %s;\n", array_type_str, genname_str);
target->source.global_vars = mputprintf(target->source.global_vars,
"%s %s;\n", array_type_str, genname_str);
target->functions.pre_init = mputstr(target->functions.pre_init, "{\n"
"static const char * const port_name = \"");
target->functions.pre_init = mputstr(target->functions.pre_init,
dispname.c_str());
target->functions.pre_init = mputprintf(target->functions.pre_init,
"\";\n"
"%s.set_name(port_name);\n"
"}\n", genname_str);
} else {
// single port
const char *type_genname_str = type_genname.c_str();
target->header.global_vars = mputprintf(target->header.global_vars,
"extern %s %s;\n", type_genname_str, genname_str);
target->source.global_vars = mputprintf(target->source.global_vars,
"%s %s(\"%s\");\n", type_genname_str, genname_str, dispname.c_str());
}
target->functions.init_comp = mputprintf(target->functions.init_comp,
"%s.activate_port();\n", genname_str);
}
void Def_Port::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
char *Def_Port::generate_code_init_comp(char *str, Definition *base_defn)
{
return mputprintf(str, "%s.activate_port();\n",
base_defn->get_genname_from_scope(my_scope).c_str());
}
void Def_Port::dump_internal(unsigned level) const
{
DEBUG(level, "Port: %s", id->get_dispname().c_str());
DEBUG(level + 1, "Port type:");
type_ref->dump(level + 2);
if (dimensions) dimensions->dump(level + 1);
}
// =================================
// ===== Def_Function_Base
// =================================
Def_Function_Base::asstype_t Def_Function_Base::determine_asstype(
bool is_external, bool has_return_type, bool returns_template)
{
if (is_external) {
if (has_return_type) {
if (returns_template) return A_EXT_FUNCTION_RTEMP;
else return A_EXT_FUNCTION_RVAL;
} else {
if (returns_template)
FATAL_ERROR("Def_Function_Base::determine_asstype()");
return A_EXT_FUNCTION;
}
} else { // not an external function
if (has_return_type) {
if (returns_template) return A_FUNCTION_RTEMP;
else return A_FUNCTION_RVAL;
} else {
if (returns_template)
FATAL_ERROR("Def_Function_Base::determine_asstype()");
return A_FUNCTION;
}
}
}
Def_Function_Base::Def_Function_Base(const Def_Function_Base& p)
: Definition(p), prototype(PROTOTYPE_NONE), input_type(0), output_type(0),
final(p.final), exceptions(p.exceptions)
{
fp_list = p.fp_list->clone();
fp_list->set_my_def(this);
return_type = p.return_type ? p.return_type->clone() : 0;
template_restriction = p.template_restriction;
}
Def_Function_Base::Def_Function_Base(bool is_external, Identifier *p_id,
FormalParList *p_fpl, Type *p_return_type, bool returns_template,
template_restriction_t p_template_restriction, bool p_final,
Common::SignatureExceptions* p_exceptions)
: Definition(determine_asstype(is_external, p_return_type != 0,
returns_template), p_id), fp_list(p_fpl), return_type(p_return_type),
prototype(PROTOTYPE_NONE), input_type(0), output_type(0),
template_restriction(p_template_restriction), final(p_final),
exceptions(p_exceptions)
{
if (!p_fpl) FATAL_ERROR("Def_Function_Base::Def_Function_Base()");
fp_list->set_my_def(this);
if (return_type) return_type->set_ownertype(Type::OT_FUNCTION_DEF, this);
}
Def_Function_Base::~Def_Function_Base()
{
delete fp_list;
delete return_type;
delete exceptions;
}
void Def_Function_Base::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
fp_list->set_fullname(p_fullname + ".<formal_par_list>");
if (return_type) return_type->set_fullname(p_fullname + ".<return_type>");
if (exceptions != NULL) {
exceptions->set_fullname(p_fullname + ".<exceptions>");
}
}
void Def_Function_Base::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
fp_list->set_my_scope(p_scope);
if (return_type) return_type->set_my_scope(p_scope);
if (exceptions != NULL) {
exceptions->set_my_scope(p_scope);
}
}
Type *Def_Function_Base::get_Type()
{
if (!checked) chk();
return return_type;
}
FormalParList *Def_Function_Base::get_FormalParList()
{
if (!checked) chk();
return fp_list;
}
const char *Def_Function_Base::get_prototype_name() const
{
switch (prototype) {
case PROTOTYPE_NONE:
return "<no prototype>";
case PROTOTYPE_CONVERT:
return "convert";
case PROTOTYPE_FAST:
return "fast";
case PROTOTYPE_BACKTRACK:
return "backtrack";
case PROTOTYPE_SLIDING:
return "sliding";
default:
return "<unknown prototype>";
}
}
void Def_Function_Base::chk_prototype()
{
switch (prototype) {
case PROTOTYPE_NONE:
// return immediately
return;
case PROTOTYPE_CONVERT:
case PROTOTYPE_FAST:
case PROTOTYPE_BACKTRACK:
case PROTOTYPE_SLIDING:
// perform the checks below
break;
default:
FATAL_ERROR("Def_Function_Base::chk_prototype()");
}
// checking the formal parameter list
if (prototype == PROTOTYPE_CONVERT) {
if (fp_list->get_nof_fps() == 1) {
FormalPar *par = fp_list->get_fp_byIndex(0);
if (par->get_asstype() == A_PAR_VAL_IN) {
input_type = par->get_Type();
} else {
par->error("The parameter must be an `in' value parameter for "
"attribute `prototype(%s)' instead of %s", get_prototype_name(),
par->get_assname());
}
} else {
fp_list->error("The function must have one parameter instead of %lu "
"for attribute `prototype(%s)'", static_cast<unsigned long>( fp_list->get_nof_fps() ),
get_prototype_name());
}
} else { // not PROTOTYPE_CONVERT
if (fp_list->get_nof_fps() == 2) {
FormalPar *first_par = fp_list->get_fp_byIndex(0);
if (prototype == PROTOTYPE_SLIDING) {
if (first_par->get_asstype() == A_PAR_VAL_INOUT) {
Type *first_par_type = first_par->get_Type();
switch (first_par_type->get_type_refd_last()
->get_typetype_ttcn3()) {
case Type::T_ERROR:
case Type::T_OSTR:
case Type::T_CSTR:
case Type::T_BSTR:
input_type = first_par_type;
break;
default:
first_par_type->error("The type of the first parameter must be "
"`octetstring' or `charstring' or `bitstring' for attribute "
"`prototype(%s)' instead of `%s'", get_prototype_name(),
first_par_type->get_typename().c_str());
}
} else {
first_par->error("The first parameter must be an `inout' value "
"parameter for attribute `prototype(%s)' instead of %s",
get_prototype_name(), first_par->get_assname());
}
} else {
if (first_par->get_asstype() == A_PAR_VAL_IN) {
input_type = first_par->get_Type();
} else {
first_par->error("The first parameter must be an `in' value "
"parameter for attribute `prototype(%s)' instead of %s",
get_prototype_name(), first_par->get_assname());
}
}
FormalPar *second_par = fp_list->get_fp_byIndex(1);
if (second_par->get_asstype() == A_PAR_VAL_OUT) {
output_type = second_par->get_Type();
} else {
second_par->error("The second parameter must be an `out' value "
"parameter for attribute `prototype(%s)' instead of %s",
get_prototype_name(), second_par->get_assname());
}
} else {
fp_list->error("The function must have two parameters for attribute "
"`prototype(%s)' instead of %lu", get_prototype_name(),
static_cast<unsigned long>( fp_list->get_nof_fps()) );
}
}
// checking the return type
if (prototype == PROTOTYPE_FAST) {
if (return_type) {
return_type->error("The function cannot have return type for "
"attribute `prototype(%s)'", get_prototype_name());
}
} else {
if (return_type) {
if (asstype == A_FUNCTION_RTEMP || asstype == A_EXT_FUNCTION_RTEMP)
return_type->error("The function must return a value instead of a "
"template for attribute `prototype(%s)'", get_prototype_name());
if (prototype == PROTOTYPE_CONVERT) {
output_type = return_type;
} else {
switch (return_type->get_type_refd_last()->get_typetype_ttcn3()) {
case Type::T_ERROR:
case Type::T_INT:
break;
default:
return_type->error("The return type of the function must be "
"`integer' instead of `%s' for attribute `prototype(%s)'",
return_type->get_typename().c_str(), get_prototype_name());
}
}
} else {
error("The function must have return type for attribute "
"`prototype(%s)'", get_prototype_name());
}
}
// checking the 'runs on' clause
if (get_RunsOnType()) {
error("The function cannot have `runs on' clause for attribute "
"`prototype(%s)'", get_prototype_name());
}
}
Type *Def_Function_Base::get_input_type()
{
if (!checked) chk();
return input_type;
}
Type *Def_Function_Base::get_output_type()
{
if (!checked) chk();
return output_type;
}
bool Def_Function_Base::is_identical(Def_Function_Base* p_other)
{
Common::Assignment::asstype_t asstype2 = p_other->get_asstype();
if (asstype != asstype2) {
if ((asstype == Common::Assignment::A_FUNCTION && asstype2 != Common::Assignment::A_EXT_FUNCTION) ||
(asstype == Common::Assignment::A_EXT_FUNCTION && asstype2 != Common::Assignment::A_FUNCTION) ||
(asstype == Common::Assignment::A_FUNCTION_RVAL && asstype2 != Common::Assignment::A_EXT_FUNCTION_RVAL) ||
(asstype == Common::Assignment::A_EXT_FUNCTION_RVAL && asstype2 != Common::Assignment::A_FUNCTION_RVAL) ||
(asstype == Common::Assignment::A_FUNCTION_RTEMP && asstype2 != Common::Assignment::A_EXT_FUNCTION_RTEMP) ||
(asstype == Common::Assignment::A_EXT_FUNCTION_RTEMP && asstype2 != Common::Assignment::A_FUNCTION_RTEMP)) {
return false;
}
}
if (return_type != NULL &&
!p_other->return_type->is_identical(return_type)) {
return false;
}
FormalParList* other_fp_list = p_other->get_FormalParList();
if (other_fp_list->get_nof_fps() != fp_list->get_nof_fps()) {
return false;
}
for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
FormalPar* fp1 = fp_list->get_fp_byIndex(i);
FormalPar* fp2 = other_fp_list->get_fp_byIndex(i);
if (fp1->get_asstype() != fp2->get_asstype() ||
!fp1->get_Type()->is_identical(fp2->get_Type()) ||
fp1->get_id().get_name() != fp2->get_id().get_name()) {
return false;
}
}
if (exceptions != NULL || p_other->exceptions != NULL) {
if (exceptions == NULL || p_other->exceptions == NULL) {
// one of them has exceptions, the other doesn't
return false;
}
if (exceptions->get_nof_types() != p_other->exceptions->get_nof_types()) {
return false;
}
for (size_t i = 0; i < exceptions->get_nof_types(); ++i) {
if (!p_other->exceptions->has_type(exceptions->get_type_byIndex(i))) {
return false;
}
}
}
return true;
}
// =================================
// ===== Def_Function
// =================================
Def_Function::Def_Function(bool p_deterministic, Identifier *p_id, FormalParList *p_fpl,
Reference *p_runs_on_ref, Reference *p_mtc_ref,
Reference *p_system_ref, Reference *p_port_ref,
Type *p_return_type,
bool returns_template,
template_restriction_t p_template_restriction,
bool p_final, Common::SignatureExceptions* p_exceptions, StatementBlock *p_block)
: Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template,
p_template_restriction, p_final, p_exceptions),
runs_on_ref(p_runs_on_ref), runs_on_type(0),
mtc_ref(p_mtc_ref), mtc_type(0),
system_ref(p_system_ref), system_type(0),
port_ref(p_port_ref), port_type(0), block(p_block),
is_startable(false), transparent(false), deterministic(p_deterministic)
{
if (!p_block) FATAL_ERROR("Def_Function::Def_Function()");
block->set_my_def(this);
}
Def_Function::~Def_Function()
{
delete runs_on_ref;
delete mtc_ref;
delete system_ref;
delete port_ref;
delete block;
}
Def_Function *Def_Function::clone() const
{
FATAL_ERROR("Def_Function::clone");
}
void Def_Function::set_fullname(const string& p_fullname)
{
Def_Function_Base::set_fullname(p_fullname);
if (runs_on_ref) runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>");
if (mtc_ref) mtc_ref->set_fullname(p_fullname + ".<mtc_type>");
if (system_ref) system_ref->set_fullname(p_fullname + ".<system_type>");
if (port_ref) port_ref->set_fullname(p_fullname + ".<port_type>");
block->set_fullname(p_fullname + ".<statement_block>");
}
void Def_Function::set_my_scope(Scope *p_scope)
{
bridgeScope.set_parent_scope(p_scope);
bridgeScope.set_scopeMacro_name(id->get_dispname());
Def_Function_Base::set_my_scope(&bridgeScope);
if (runs_on_ref) runs_on_ref->set_my_scope(&bridgeScope);
if (mtc_ref) mtc_ref->set_my_scope(&bridgeScope);
if (system_ref) system_ref->set_my_scope(&bridgeScope);
if (port_ref) port_ref->set_my_scope(&bridgeScope);
block->set_my_scope(fp_list);
}
Type *Def_Function::get_MtcType()
{
if (!checked) chk();
return mtc_type;
}
Type *Def_Function::get_SystemType()
{
if (!checked) chk();
return system_type;
}
Type *Def_Function::get_RunsOnType()
{
if (!checked) chk();
return runs_on_type;
}
Type *Def_Function::get_PortType()
{
if (!checked) chk();
return port_type;
}
RunsOnScope *Def_Function::get_runs_on_scope(Type *comptype)
{
Module *my_module = dynamic_cast<Module*>(my_scope->get_scope_mod());
if (!my_module) FATAL_ERROR("Def_Function::get_runs_on_scope()");
return my_module->get_runs_on_scope(comptype);
}
PortScope *Def_Function::get_port_scope(Type *porttype)
{
Module *my_module = dynamic_cast<Module*>(my_scope->get_scope_mod());
if (!my_module) FATAL_ERROR("Def_Function::get_port_scope()");
return my_module->get_port_scope(porttype);
}
void Def_Function::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In function definition `%s'",
id->get_dispname().c_str());
if (deterministic) {
note("Please ensure that the `%s' function complies with the requirements"
" in clause 16.1.4 of the TTCN-3 core language standard (ES 201 873-1)",
id->get_dispname().c_str());
}
// `runs on' clause and `port' clause are mutually exclusive
if (runs_on_ref && port_ref) {
runs_on_ref->error("A `runs on' and a `port' clause cannot be present at the same time.");
}
if (my_scope->is_class_scope()) {
// class methods inherit `runs on', `mtc' and `system' clauses from the class
ClassTypeBody* class_ = my_scope->get_scope_class();
if (class_->is_trait()) {
error("Trait class type `%s' cannot have non-abstract methods",
class_->get_my_def()->get_Type()->get_typename().c_str());
}
runs_on_type = class_->get_RunsOnType();
mtc_type = class_->get_MtcType();
system_type = class_->get_SystemType();
}
else { // not in a class method
// checking the `runs on' clause
if (runs_on_ref) {
Error_Context cntxt2(runs_on_ref, "In `runs on' clause");
runs_on_type = runs_on_ref->chk_comptype_ref();
}
// checking the `mtc' clause
if (mtc_ref) {
Error_Context cntxt2(mtc_ref, "In `mtc' clause");
mtc_type = mtc_ref->chk_comptype_ref();
}
// checking the `system' clause
if (system_ref) {
Error_Context cntxt2(system_ref, "In `system' clause");
system_type = system_ref->chk_comptype_ref();
}
}
// create scope units for the `runs on', `mtc' and `system' components,
// and link them in a row between the function's scope and the
// formal parameter list's scope
Scope* current_scope = my_scope;
if (system_type != NULL) {
Scope *system_scope = get_runs_on_scope(system_type);
system_scope->set_parent_scope(current_scope);
current_scope = system_scope;
}
if (mtc_type != NULL) {
Scope *mtc_scope = get_runs_on_scope(mtc_type);
mtc_scope->set_parent_scope(current_scope);
current_scope = mtc_scope;
}
if (runs_on_type != NULL) {
Scope *runs_on_scope = get_runs_on_scope(runs_on_type);
runs_on_scope->set_parent_scope(current_scope);
current_scope = runs_on_scope;
}
fp_list->set_my_scope(current_scope);
// checking the formal parameter list, the check must come before the
// chk_prototype() function call.
fp_list->chk(asstype);
// checking of return type
if (return_type) {
Error_Context cntxt2(return_type, "In return type");
return_type->chk();
return_type->chk_as_return_type(asstype == A_FUNCTION_RVAL," function");
}
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
Ttcn::ExtensionAttributes * extattrs = parse_extattributes(w_attrib_path);
if (extattrs != 0) { // NULL means parsing error
size_t num_atrs = extattrs->size();
for (size_t i=0; i < num_atrs; ++i) {
ExtensionAttribute &ea = extattrs->get(i);
switch (ea.get_type()) {
case ExtensionAttribute::PROTOTYPE: {
if (get_prototype() != Def_Function_Base::PROTOTYPE_NONE) {
ea.error("Duplicate attribute `prototype'");
}
Def_Function_Base::prototype_t proto = ea.get_proto();
set_prototype(proto);
break; }
case ExtensionAttribute::ANYTYPELIST: // ignore it
case ExtensionAttribute::NONE: // erroneous, do not issue an error
break;
case ExtensionAttribute::TRANSPARENT:
transparent = true;
break;
case ExtensionAttribute::ENCODE:
case ExtensionAttribute::DECODE:
case ExtensionAttribute::ERRORBEHAVIOR:
case ExtensionAttribute::PRINTING:
ea.error("Extension attribute 'encode', 'decode', 'errorbehavior'"
" or 'printing' can only be applied to external functions");
// fall through
default: // complain
ea.error("Function definition can only have the 'prototype'"
" extension attribute");
break;
}
}
delete extattrs;
}
}
chk_prototype();
// checking the `port' clause
if (port_ref) {
Error_Context cntxt2(port_ref, "In `port' clause");
Assignment *ass = port_ref->get_refd_assignment();
if (ass) {
port_type = ass->get_Type();
if (port_type) {
switch (port_type->get_typetype()) {
case Type::T_PORT: {
Scope *port_scope = get_port_scope(port_type);
port_scope->set_parent_scope(my_scope);
fp_list->set_my_scope(port_scope);
break; }
default:
port_ref->error(
"Reference `%s' does not refer to a port type.",
port_ref->get_dispname().c_str());
}
} else {
FATAL_ERROR("Def_Function::chk()");
}
}
}
// decision of startability
is_startable = runs_on_ref != 0 && !my_scope->is_class_scope();
if (is_startable && !fp_list->get_startability()) is_startable = false;
if (is_startable && return_type && return_type->is_component_internal())
is_startable = false;
// checking of statement block
block->chk();
if (return_type) {
// checking the presence of return statements
switch (block->has_return()) {
case StatementBlock::RS_NO:
error("The function has return type, but it does not have any return "
"statement");
break;
case StatementBlock::RS_MAYBE:
error("The function has return type, but control might leave it "
"without reaching a return statement");
default:
break;
}
}
if (exceptions != NULL) {
exceptions->chk(this);
}
if (!semantic_check_only) {
fp_list->set_genname(get_genname());
block->set_code_section(GovernedSimple::CS_INLINE);
}
}
bool Def_Function::chk_startable(Location* caller_location)
{
if (!checked) chk();
if (my_scope->is_class_scope()) {
caller_location->error("A method of a class cannot be started on a parallel test component");
return false;
}
fp_list->chk_startability("Function", get_fullname().c_str(), caller_location);
if (is_startable) return true;
if (!runs_on_ref) error("Function `%s' cannot be started on a parallel "
"test component because it does not have `runs on' clause",
get_fullname().c_str());
if (return_type && return_type->is_component_internal()) {
map<Type*,void> type_chain;
char* err_str = mprintf("the return type or embedded in the return type "
"of function `%s' if it is started on a parallel test component",
get_fullname().c_str());
return_type->chk_component_internal(type_chain, err_str);
Free(err_str);
}
return false;
}
void Def_Function::generate_code(output_struct *target, bool clean_up)
{
// Functions with 'port' clause are generated into the port type's class def
// Reuse of clean_up variable to allow or disallow the generation.
// clean_up is true when it is called from PortTypeBody::generate_code())
if (port_type && !clean_up) {
return;
}
transparency_holder glass(*this);
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const char *dispname_str = id->get_dispname().c_str();
string return_type_name;
switch (asstype) {
case A_FUNCTION:
return_type_name = "void";
break;
case A_FUNCTION_RVAL:
return_type_name = return_type->get_genname_value(my_scope);
break;
case A_FUNCTION_RTEMP:
return_type_name = return_type->get_genname_template(my_scope);
break;
default:
FATAL_ERROR("Def_Function::generate_code()");
}
const char *return_type_str = return_type_name.c_str();
// smart formal parameter list (names of unused parameters are omitted)
char *formal_par_list = fp_list->generate_code(memptystr());
fp_list->generate_code_defval(target);
bool in_class = my_scope->is_class_scope();
// in case of classes call fp_list->generate_code_defval, as that may make shadow objects unnecessary
char* defpar_init_code = in_class ? fp_list->generate_code_defpar_init(memptystr()) : memptystr();
char* shadow_objects = fp_list->generate_shadow_objects(memptystr());
// assemble the function body first (this also determines which parameters
// are never used)
char* body = create_location_object(memptystr(), "FUNCTION", dispname_str);
if (!enable_set_bound_out_param)
body = fp_list->generate_code_set_unbound(body); // conform the standard out parameter is unbound
if (debugger_active) {
body = generate_code_debugger_function_init(body, this);
}
body = block->generate_code(body, target->header.global_vars,
target->source.global_vars);
char*& header = in_class ? target->header.class_defs :
target->header.function_prototypes;
char*& source = in_class ? target->source.methods :
target->source.function_bodies;
// function prototype
header = mputprintf(header, "%s%s %s(%s);\n",
get_PortType() && clean_up ? "" : (in_class ? "virtual " : "extern "),
return_type_str, genname_str, formal_par_list);
// function body
source = mputprintf(source,
"%s %s%s%s%s(%s)\n"
"{\n"
"%s%s%s"
"}\n\n",
return_type_str,
port_type && clean_up ? port_type->get_genname_own().c_str() :
(in_class ? my_scope->get_scope_class()->get_id()->get_name().c_str() : ""),
port_type && clean_up && port_type->get_PortBody()->get_testport_type() != PortTypeBody::TP_INTERNAL ? "_BASE" : "",
in_class || (port_type && clean_up) ? "::" : "",
genname_str, formal_par_list, defpar_init_code, shadow_objects, body);
Free(formal_par_list);
Free(body);
Free(defpar_init_code);
Free(shadow_objects);
if (is_startable && !in_class) {
size_t nof_fps = fp_list->get_nof_fps();
// use the full list of formal parameters here (since they are all logged)
char *full_formal_par_list = fp_list->generate_code(memptystr(), nof_fps);
// starter function (stub)
// function prototype
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern void start_%s(const COMPONENT& component_reference%s%s);\n",
genname_str, nof_fps>0?", ":"", full_formal_par_list);
// function body
body = mprintf("void start_%s(const COMPONENT& component_reference%s"
"%s)\n"
"{\n"
"TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_PTC);\n"
"TTCN_Logger::log_event_str(\"Starting function %s(\");\n",
genname_str, nof_fps>0?", ":"", full_formal_par_list, dispname_str);
for (size_t i = 0; i < nof_fps; i++) {
if (i > 0) body = mputstr(body,
"TTCN_Logger::log_event_str(\", \");\n");
body = mputprintf(body, "%s.log();\n",
fp_list->get_fp_byIndex(i)->get_reference_name(my_scope).c_str());
}
body = mputprintf(body,
"TTCN_Logger::log_event_str(\") on component \");\n"
"component_reference.log();\n"
"TTCN_Logger::log_char('.');\n"
"TTCN_Logger::end_event();\n"
"Text_Buf text_buf;\n"
"TTCN_Runtime::prepare_start_component(component_reference, "
"\"%s\", \"%s\", text_buf);\n",
my_scope->get_scope_mod()->get_modid().get_dispname().c_str(),
dispname_str);
for (size_t i = 0; i < nof_fps; i++) {
body = mputprintf(body, "%s.encode_text(text_buf);\n",
fp_list->get_fp_byIndex(i)->get_reference_name(my_scope).c_str());
}
body = mputstr(body, "TTCN_Runtime::send_start_component(text_buf);\n"
"}\n\n");
target->source.function_bodies = mputstr(target->source.function_bodies,
body);
Free(body);
// an entry in start_ptc_function
body = mprintf("if (!strcmp(function_name, \"%s\")) {\n",
dispname_str);
if (nof_fps > 0) {
body = fp_list->generate_code_object(body, "", ' ', true);
for (size_t i = 0; i < nof_fps; i++) {
body = mputprintf(body, "%s.decode_text(function_arguments);\n",
fp_list->get_fp_byIndex(i)->get_reference_name(my_scope).c_str());
}
body = mputprintf(body,
"TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_PTC);\n"
"TTCN_Logger::log_event_str(\"Starting function %s(\");\n",
dispname_str);
for (size_t i = 0; i < nof_fps; i++) {
if (i > 0) body = mputstr(body,
"TTCN_Logger::log_event_str(\", \");\n");
body = mputprintf(body, "%s.log();\n",
fp_list->get_fp_byIndex(i)->get_reference_name(my_scope).c_str());
}
body = mputstr(body, "TTCN_Logger::log_event_str(\").\");\n"
"TTCN_Logger::end_event();\n");
} else {
body = mputprintf(body,
"TTCN_Logger::log_str(TTCN_Logger::PARALLEL_PTC, \"Starting function "
"%s().\");\n", dispname_str);
}
body = mputstr(body,
"TTCN_Runtime::function_started(function_arguments);\n");
char *actual_par_list =
fp_list->generate_code_actual_parlist(memptystr(), "");
bool return_value_kept = false;
if (asstype == A_FUNCTION_RVAL) {
// the return value is kept only if the function returns a value
// (rather than a template) and the return type has the "done"
// extension attribute
for (Type *t = return_type; ; t = t->get_type_refd()) {
if (t->has_done_attribute()) {
return_value_kept = true;
break;
} else if (!t->is_ref()) break;
}
}
if (return_value_kept) {
const string& return_type_dispname = return_type->get_typename();
const char *return_type_dispname_str = return_type_dispname.c_str();
body = mputprintf(body, "%s ret_val(%s(%s));\n"
"TTCN_Logger::begin_event(TTCN_PARALLEL);\n"
"TTCN_Logger::log_event_str(\"Function %s returned %s : \");\n"
"ret_val.log();\n"
"Text_Buf text_buf;\n"
"TTCN_Runtime::prepare_function_finished(\"%s\", text_buf);\n"
"ret_val.encode_text(text_buf);\n"
"TTCN_Runtime::send_function_finished(text_buf);\n",
return_type_str, genname_str, actual_par_list, dispname_str,
return_type_dispname_str, return_type_dispname_str);
} else {
body = mputprintf(body, "%s(%s);\n"
"TTCN_Runtime::function_finished(\"%s\");\n",
genname_str, actual_par_list, dispname_str);
}
Free(actual_par_list);
body = mputstr(body, "return TRUE;\n"
"} else ");
target->functions.start = mputstr(target->functions.start, body);
Free(body);
Free(full_formal_par_list);
}
if (!in_class) {
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s.add_function(\"%s\", (genericfunc_t)&%s, ", get_module_object_name(),
dispname_str, genname_str);
if(is_startable)
target->functions.pre_init = mputprintf(target->functions.pre_init,
"(genericfunc_t)&start_%s);\n", genname_str);
else
target->functions.pre_init = mputstr(target->functions.pre_init,
"NULL);\n");
}
}
void Def_Function::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
void Def_Function::dump_internal(unsigned level) const
{
DEBUG(level, "Function: %s", id->get_dispname().c_str());
DEBUG(level + 1, "Parameters:");
fp_list->dump(level + 1);
if (runs_on_ref) {
DEBUG(level + 1, "Runs on clause:");
runs_on_ref->dump(level + 2);
}
if (return_type) {
DEBUG(level + 1, "Return type:");
return_type->dump(level + 2);
if (asstype == A_FUNCTION_RTEMP) DEBUG(level + 1, "Returns template");
}
DEBUG(level + 1, "Deterministic: %s", deterministic ? "true" : "false");
if (prototype != PROTOTYPE_NONE)
DEBUG(level + 1, "Prototype: %s", get_prototype_name());
if (exceptions != NULL) {
DEBUG(level + 1, "Exceptions:");
exceptions->dump(level + 2);
}
//DEBUG(level + 1, "Statement block:");
block->dump(level + 1);
}
void Def_Function::set_parent_path(WithAttribPath* p_path) {
Def_Function_Base::set_parent_path(p_path);
block->set_parent_path(w_attrib_path);
}
// =================================
// ===== Def_ExtFunction
// =================================
Def_ExtFunction::~Def_ExtFunction()
{
delete encoding_options;
delete eb_list;
if (NULL != printing) {
delete printing;
}
}
Def_ExtFunction *Def_ExtFunction::clone() const
{
FATAL_ERROR("Def_ExtFunction::clone");
}
void Def_ExtFunction::set_fullname(const string& p_fullname)
{
Def_Function_Base::set_fullname(p_fullname);
if (eb_list) eb_list->set_fullname(p_fullname + ".<errorbehavior_list>");
}
void Def_ExtFunction::set_encode_parameters(Type::MessageEncodingType_t
p_encoding_type, string *p_encoding_options)
{
function_type = EXTFUNC_ENCODE;
encoding_type = p_encoding_type;
delete encoding_options;
encoding_options = p_encoding_options;
}
void Def_ExtFunction::set_decode_parameters(Type::MessageEncodingType_t
p_encoding_type, string *p_encoding_options)
{
function_type = EXTFUNC_DECODE;
encoding_type = p_encoding_type;
delete encoding_options;
encoding_options = p_encoding_options;
}
void Def_ExtFunction::add_eb_list(Ttcn::ErrorBehaviorList *p_eb_list)
{
if (!p_eb_list) FATAL_ERROR("Def_ExtFunction::add_eb_list()");
if (eb_list) {
eb_list->steal_ebs(p_eb_list);
delete p_eb_list;
} else {
eb_list = p_eb_list;
eb_list->set_fullname(get_fullname() + ".<errorbehavior_list>");
}
}
void Def_ExtFunction::chk_function_type()
{
switch (function_type) {
case EXTFUNC_MANUAL:
if (eb_list) {
eb_list->error("Attribute `errorbehavior' can only be used together "
"with `encode' or `decode'");
eb_list->chk();
}
break;
case EXTFUNC_ENCODE:
switch (prototype) {
case PROTOTYPE_NONE:
error("Attribute `encode' cannot be used without `prototype'");
break;
case PROTOTYPE_BACKTRACK:
case PROTOTYPE_SLIDING:
error("Attribute `encode' cannot be used with `prototype(%s)'",
get_prototype_name());
default: /* CONVERT and FAST allowed */
break;
}
if (input_type) {
if (!input_type->has_encoding(encoding_type, encoding_options)) {
if (Common::Type::CT_CUSTOM == encoding_type) {
input_type->error("Input type `%s' does not support custom encoding '%s'",
input_type->get_typename().c_str(), encoding_options->c_str());
}
else {
// First collect the fields of a record, set, union type which
// does not support the encoding, then write it in the error message.
char* message = NULL;
if (legacy_codec_handling) {
Type *t = input_type;
if (t->is_ref()) t = t->get_type_refd();
switch(t->get_typetype()) {
case Type::T_SEQ_T:
case Type::T_SET_T:
case Type::T_CHOICE_T: {
for (size_t i = 0; i < t->get_nof_comps(); i++) {
if (!t->get_comp_byIndex(i)->get_type()->has_encoding(encoding_type, encoding_options)) {
if (i == 0) {
message = mputprintf(message, " The following fields do not support %s encoding: ",
Type::get_encoding_name(encoding_type));
} else {
message = mputstr(message, ", ");
}
message = mputstr(message, t->get_comp_id_byIndex(i).get_ttcnname().c_str());
}
}
break; }
default:
break;
}
}
input_type->error("Input type `%s' does not support %s encoding.%s",
input_type->get_typename().c_str(),
Type::get_encoding_name(encoding_type), message == NULL ? "" : message);
Free(message);
}
}
else {
if (Common::Type::CT_XER == encoding_type) {
Type* last = input_type->get_type_refd_last();
if (last->is_untagged() &&
((last->get_typetype() != Type::T_CHOICE_A &&
last->get_typetype() != Type::T_CHOICE_T &&
last->get_typetype() != Type::T_ANYTYPE) || legacy_untagged_union == TRUE)) {
// "untagged" on the (toplevel) input type will have no effect,
// unless it is union or anytype
warning("UNTAGGED encoding attribute is ignored on top-level type");
}
}
if (Common::Type::CT_CUSTOM == encoding_type ||
Common::Type::CT_PER == encoding_type) {
if (PROTOTYPE_CONVERT != prototype) {
error("Only `prototype(convert)' is allowed for %s encoding functions",
Type::get_encoding_name(encoding_type));
}
else if (input_type->is_ref()) {
// let the input type know that this is its encoding function
if (legacy_codec_handling) {
input_type->get_type_refd()->set_legacy_coding_function(true, this);
}
else {
input_type->get_type_refd()->set_coding_function(
encoding_type == Common::Type::CT_PER ? "PER" :
encoding_options->c_str(), TRUE, this);
}
// treat this as a manual external function during code generation
function_type = EXTFUNC_MANUAL;
}
}
else if (legacy_codec_handling && // for now, this only works with legacy codec handling
input_type->is_ref() && input_type->get_type_refd()->is_asn1()) {
// let the input ASN.1 type know that this is its encoding type
input_type->get_type_refd()->set_asn_coding(true, encoding_type);
}
}
}
if (output_type) {
if(encoding_type == Common::Type::CT_TEXT) {
// TEXT encoding supports bitstring, octetstring and charstring stream types
Type *stream_type = Type::get_stream_type(encoding_type,0);
Type *stream_type2 = Type::get_stream_type(encoding_type,1);
Type *stream_type3 = Type::get_stream_type(encoding_type,2);
if ( (!stream_type->is_identical(output_type)) &&
(!stream_type2->is_identical(output_type)) &&
(!stream_type3->is_identical(output_type))) {
output_type->error("The output type of %s encoding should be `%s', "
"`%s' or `%s' instead of `%s'", Type::get_encoding_name(encoding_type),
stream_type->get_typename().c_str(),
stream_type2->get_typename().c_str(),
stream_type3->get_typename().c_str(),
output_type->get_typename().c_str());
}
}
else if (encoding_type == Common::Type::CT_CUSTOM ||
encoding_type == Common::Type::CT_PER) {
// custom and PER encodings only support the bitstring stream type
Type *stream_type = Type::get_stream_type(encoding_type);
if (!stream_type->is_identical(output_type)) {
output_type->error("The output type of %s encoding should be `%s' "
"instead of `%s'", Type::get_encoding_name(encoding_type),
stream_type->get_typename().c_str(),
output_type->get_typename().c_str());
}
}
else {
// all other encodings support bitstring and octetstring stream types
Type *stream_type = Type::get_stream_type(encoding_type, 0);
Type *stream_type2 = Type::get_stream_type(encoding_type, 1);
if (!stream_type->is_identical(output_type) &&
!stream_type2->is_identical(output_type)) {
output_type->error("The output type of %s encoding should be `%s' "
"or '%s' instead of `%s'", Type::get_encoding_name(encoding_type),
stream_type->get_typename().c_str(),
stream_type2->get_typename().c_str(),
output_type->get_typename().c_str());
}
}
}
if (eb_list) eb_list->chk();
chk_allowed_encode();
break;
case EXTFUNC_DECODE:
if (prototype == PROTOTYPE_NONE) {
error("Attribute `decode' cannot be used without `prototype'");
}
if (input_type) {
if(encoding_type == Common::Type::CT_TEXT) {
// TEXT encoding supports bitstring, octetstring and charstring stream types
Type *stream_type = Type::get_stream_type(encoding_type,0);
Type *stream_type2 = Type::get_stream_type(encoding_type,1);
Type *stream_type3 = Type::get_stream_type(encoding_type,2);
if ( (!stream_type->is_identical(input_type)) &&
(!stream_type2->is_identical(input_type)) &&
(!stream_type3->is_identical(input_type))) {
input_type->error("The input type of %s decoding should be `%s', "
"`%s' or `%s' instead of `%s'", Type::get_encoding_name(encoding_type),
stream_type->get_typename().c_str(),
stream_type2->get_typename().c_str(),
stream_type3->get_typename().c_str(),
input_type->get_typename().c_str());
}
}
else if (encoding_type == Common::Type::CT_CUSTOM ||
encoding_type == Common::Type::CT_PER) {
// custom and PER encodings only support the bitstring stream type
Type *stream_type = Type::get_stream_type(encoding_type);
if (!stream_type->is_identical(input_type)) {
input_type->error("The input type of %s decoding should be `%s' "
"instead of `%s'", Type::get_encoding_name(encoding_type),
stream_type->get_typename().c_str(),
input_type->get_typename().c_str());
}
}
else {
// all other encodings support bitstring and octetstring stream types
Type *stream_type = Type::get_stream_type(encoding_type, 0);
Type *stream_type2 = Type::get_stream_type(encoding_type, 1);
if (!stream_type->is_identical(input_type) &&
!stream_type2->is_identical(input_type)) {
input_type->error("The input type of %s decoding should be `%s' "
"or `%s' instead of `%s'", Type::get_encoding_name(encoding_type),
stream_type->get_typename().c_str(),
stream_type2->get_typename().c_str(),
input_type->get_typename().c_str());
}
}
}
if (output_type && !output_type->has_encoding(encoding_type, encoding_options)) {
if (Common::Type::CT_CUSTOM == encoding_type) {
output_type->error("Output type `%s' does not support custom encoding '%s'",
output_type->get_typename().c_str(), encoding_options->c_str());
}
else {
output_type->error("Output type `%s' does not support %s encoding",
output_type->get_typename().c_str(),
Type::get_encoding_name(encoding_type));
}
}
else {
if (Common::Type::CT_CUSTOM == encoding_type ||
Common::Type::CT_PER == encoding_type) {
if (PROTOTYPE_SLIDING != prototype) {
error("Only `prototype(sliding)' is allowed for %s decoding functions",
Type::get_encoding_name(encoding_type));
}
else if (output_type != NULL && output_type->is_ref()) {
// let the output type know that this is its decoding function
if (legacy_codec_handling) {
output_type->get_type_refd()->set_legacy_coding_function(false, this);
}
else {
output_type->get_type_refd()->set_coding_function(
encoding_type == Common::Type::CT_PER ? "PER" :
encoding_options->c_str(), FALSE, this);
}
// treat this as a manual external function during code generation
function_type = EXTFUNC_MANUAL;
}
}
else if (legacy_codec_handling && // for now, this only works with legacy codec handling
output_type != NULL && output_type->is_ref() &&
output_type->get_type_refd()->is_asn1()) {
// let the output ASN.1 type know that this is its decoding type
output_type->get_type_refd()->set_asn_coding(false, encoding_type);
}
}
if (eb_list) eb_list->chk();
chk_allowed_encode();
break;
default:
FATAL_ERROR("Def_ExtFunction::chk()");
}
if (deterministic) {
note("Please ensure that the `%s' external function complies with the"
" requirements in clause 16.1.4 of the TTCN-3 core language"
" standard (ES 201 873-1)",
id->get_dispname().c_str());
}
}
void Def_ExtFunction::chk_allowed_encode()
{
switch (encoding_type) {
case Type::CT_BER:
if (enable_ber()) return; // ok
break;
case Type::CT_RAW:
if (enable_raw()) return; // ok
break;
case Type::CT_TEXT:
if (enable_text()) return; // ok
break;
case Type::CT_XER:
if (enable_xer()) return; // ok
break;
case Type::CT_PER:
if (enable_per()) return; // ok?
break;
case Type::CT_JSON:
if (enable_json()) return;
break;
case Type::CT_OER:
if (enable_oer()) return;
break;
case Type::CT_CUSTOM:
return; // cannot be disabled
default:
FATAL_ERROR("Def_ExtFunction::chk_allowed_encode");
break;
}
error("%s encoding is disallowed by license or commandline options",
Type::get_encoding_name(encoding_type));
}
void Def_ExtFunction::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In external function definition `%s'",
id->get_dispname().c_str());
ClassTypeBody* my_class = my_scope->get_scope_class();
if (!ext_keyword && !my_class->is_external()) {
error("Missing function body or `external' keyword");
}
if (my_class != NULL && my_class->is_trait()) {
error("Trait class type `%s' cannot have non-abstract methods",
my_class->get_my_def()->get_Type()->get_typename().c_str());
}
fp_list->chk(asstype);
if (return_type) {
Error_Context cntxt2(return_type, "In return type");
return_type->chk();
return_type->chk_as_return_type(asstype == A_EXT_FUNCTION_RVAL,
" external function");
}
if (!semantic_check_only) fp_list->set_genname(get_genname());
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
const Ttcn::ExtensionAttributes * extattrs = parse_extattributes(w_attrib_path);
if (extattrs != 0) {
size_t num_atrs = extattrs->size();
for (size_t i=0; i < num_atrs; ++i) {
ExtensionAttribute &ea = extattrs->get(i);
switch (ea.get_type()) {
case ExtensionAttribute::PROTOTYPE: {
if (get_prototype() != Def_Function_Base::PROTOTYPE_NONE) {
ea.error("Duplicate attribute `prototype'");
}
Def_Function_Base::prototype_t proto = ea.get_proto();
set_prototype(proto);
break; }
case ExtensionAttribute::ENCODE: {
switch (get_function_type()) {
case Def_ExtFunction::EXTFUNC_MANUAL:
break;
case Def_ExtFunction::EXTFUNC_ENCODE: {
ea.error("Duplicate attribute `encode'");
break; }
case Def_ExtFunction::EXTFUNC_DECODE: {
ea.error("Attributes `decode' and `encode' "
"cannot be used at the same time");
break; }
default:
FATAL_ERROR("coding_attrib_parse(): invalid external function type");
}
Type::MessageEncodingType_t et;
string *opt;
ea.get_encdec_parameters(et, opt);
set_encode_parameters(et, opt);
break; }
case ExtensionAttribute::ERRORBEHAVIOR: {
add_eb_list(ea.get_eb_list());
break; }
case ExtensionAttribute::DECODE: {
switch (get_function_type()) {
case Def_ExtFunction::EXTFUNC_MANUAL:
break;
case Def_ExtFunction::EXTFUNC_ENCODE: {
ea.error("Attributes `encode' and `decode' "
"cannot be used at the same time");
break; }
case Def_ExtFunction::EXTFUNC_DECODE: {
ea.error("Duplicate attribute `decode'");
break; }
default:
FATAL_ERROR("coding_attrib_parse(): invalid external function type");
}
Type::MessageEncodingType_t et;
string *opt;
ea.get_encdec_parameters(et, opt);
set_decode_parameters(et, opt);
break; }
case ExtensionAttribute::PRINTING: {
printing = ea.get_printing();
break; }
case ExtensionAttribute::ANYTYPELIST:
// ignore, because we can't distinguish between a local
// "extension anytype" (which is bogus) and an inherited one
// (which was meant for a type definition)
break;
case ExtensionAttribute::NONE:
// Ignore, do not issue "wrong type" error
break;
default:
ea.error(
"Only the following extension attributes may be applied to "
"external functions: 'prototype', 'encode', 'decode', 'errorbehavior'");
break;
} // switch type
} // next attribute
delete extattrs;
} // if extatrs
}
chk_prototype();
chk_function_type();
if (NULL != printing && (EXTFUNC_ENCODE != function_type ||
(Type::CT_JSON != encoding_type && Type::CT_XER != encoding_type))) {
error("Attribute 'printing' is only allowed for JSON and XER encoding functions.");
}
if (exceptions != NULL) {
exceptions->chk(this);
if (function_type != EXTFUNC_MANUAL) {
error("Exception list is only allowed for manually written external functions");
}
}
}
char *Def_ExtFunction::generate_code_encode(char *str)
{
const char *function_name = id->get_dispname().c_str();
const char *first_par_name =
fp_list->get_fp_byIndex(0)->get_id().get_name().c_str();
// producing debug printout of the input PDU
str = mputprintf(str,
#ifndef NDEBUG
"// written by %s in " __FILE__ " at %d\n"
#endif
"if (TTCN_Logger::log_this_event(TTCN_Logger::DEBUG_ENCDEC)) {\n"
"TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);\n"
"TTCN_Logger::log_event_str(\"%s(): Encoding %s: \");\n"
"%s.log();\n"
"TTCN_Logger::end_event();\n"
"}\n"
#ifndef NDEBUG
, __FUNCTION__, __LINE__
#endif
, function_name, input_type->get_typename().c_str(), first_par_name);
// setting error behavior
if (eb_list) str = eb_list->generate_code(str);
else str = mputstr(str, "TTCN_EncDec::set_error_behavior("
"TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);\n");
// encoding PDU into the buffer
str = mputstr(str, "TTCN_Buffer ttcn_buffer;\n");
str = mputprintf(str, "%s.encode(%s_descr_, ttcn_buffer, TTCN_EncDec::CT_%s",
first_par_name,
input_type->get_genname_typedescriptor(my_scope).c_str(),
Type::get_encoding_name(encoding_type));
if (encoding_type == Type::CT_JSON) {
if (printing != NULL) {
str = printing->generate_code(str);
} else {
str = mputstr(str, ", 0");
}
} else if (encoding_type == Type::CT_XER) {
bool compact_printing =
printing != NULL && printing->get_printing() == PrintingType::PT_COMPACT;
if (compact_printing || encoding_options) {
str = mputstr(str, ", ");
if (compact_printing) {
str = mputprintf(str, "XER_CANONICAL%s",
encoding_options ? "|" : "");
}
if (encoding_options) str = mputprintf(str, "%s",
encoding_options->c_str());
} else {
str = mputstr(str, ", 0");
}
} else {
if (encoding_options) {
str = mputprintf(str, ", %s", encoding_options->c_str());
} else {
str = mputstr(str, ", 0");
}
}
str = mputstr(str, ");\n");
const char *result_name;
switch (prototype) {
case PROTOTYPE_CONVERT:
result_name = "ret_val";
// creating a local variable for the result stream
str = mputprintf(str, "%s ret_val;\n",
output_type->get_genname_value(my_scope).c_str());
break;
case PROTOTYPE_FAST:
result_name = fp_list->get_fp_byIndex(1)->get_id().get_name().c_str();
break;
default:
FATAL_ERROR("Def_ExtFunction::generate_code_encode()");
result_name = 0;
}
// taking the result from the buffer and producing debug printout
if (output_type->get_type_refd_last()->get_typetype_ttcn3() ==
Common::Type::T_BSTR) {
// cannot extract a bitstring from the buffer, use temporary octetstring
// and convert it to bitstring
str = mputprintf(str,
"OCTETSTRING tmp_os;\n"
"ttcn_buffer.get_string(tmp_os);\n"
"%s = oct2bit(tmp_os);\n", result_name);
}
else {
str = mputprintf(str, "ttcn_buffer.get_string(%s);\n", result_name);
}
str = mputprintf(str,
"if (TTCN_Logger::log_this_event(TTCN_Logger::DEBUG_ENCDEC)) {\n"
"TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);\n"
"TTCN_Logger::log_event_str(\"%s(): Stream after encoding: \");\n"
"%s.log();\n"
"TTCN_Logger::end_event();\n"
"}\n", function_name, result_name);
// returning the result stream if necessary
if (prototype == PROTOTYPE_CONVERT) {
if (debugger_active) {
str = mputstr(str,
"ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
"ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
}
str = mputstr(str, "return ret_val;\n");
}
return str;
}
char *Def_ExtFunction::generate_code_decode(char *str)
{
const char *function_name = id->get_dispname().c_str();
const char *first_par_name =
fp_list->get_fp_byIndex(0)->get_id().get_name().c_str();
// producing debug printout of the input stream
str = mputprintf(str,
#ifndef NDEBUG
"// written by %s in " __FILE__ " at %d\n"
#endif
"if (TTCN_Logger::log_this_event(TTCN_Logger::DEBUG_ENCDEC)) {\n"
"TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);\n"
"TTCN_Logger::log_event_str(\"%s(): Stream before decoding: \");\n"
"%s.log();\n"
"TTCN_Logger::end_event();\n"
"}\n"
#ifndef NDEBUG
, __FUNCTION__, __LINE__
#endif
, function_name, first_par_name);
// setting error behavior
if (eb_list) str = eb_list->generate_code(str);
else if (prototype == PROTOTYPE_BACKTRACK || prototype == PROTOTYPE_SLIDING) {
str = mputstr(str, "TTCN_EncDec::set_error_behavior("
"TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);\n");
} else str = mputstr(str, "TTCN_EncDec::set_error_behavior("
"TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);\n");
str = mputstr(str, "TTCN_EncDec::clear_error();\n");
// creating a buffer from the input stream
if (input_type->get_type_refd_last()->get_typetype_ttcn3() ==
Common::Type::T_BSTR) {
// cannot create a buffer from a bitstring, convert it to octetstring
str = mputprintf(str, "TTCN_Buffer ttcn_buffer(bit2oct(%s));\n",
first_par_name);
}
else {
str = mputprintf(str, "TTCN_Buffer ttcn_buffer(%s);\n", first_par_name);
}
const char *result_name;
if (prototype == PROTOTYPE_CONVERT) {
// creating a local variable for the result
str = mputprintf(str, "%s ret_val;\n",
output_type->get_genname_value(my_scope).c_str());
result_name = "ret_val";
} else {
result_name = fp_list->get_fp_byIndex(1)->get_id().get_name().c_str();
}
if(encoding_type==Type::CT_TEXT){
str = mputprintf(str,
"if (TTCN_Logger::log_this_event(TTCN_Logger::DEBUG_ENCDEC)) {\n"
" TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_LOG_MATCHING, TTCN_EncDec::EB_WARNING);\n"
"}\n");
}
str = mputprintf(str, "%s.decode(%s_descr_, ttcn_buffer, "
"TTCN_EncDec::CT_%s", result_name,
output_type->get_genname_typedescriptor(my_scope).c_str(),
Type::get_encoding_name(encoding_type));
if (encoding_options) str = mputprintf(str, ", %s",
encoding_options->c_str());
str = mputstr(str, ");\n");
// producing debug printout of the result PDU
str = mputprintf(str,
"if (TTCN_Logger::log_this_event(TTCN_Logger::DEBUG_ENCDEC)) {\n"
"TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);\n"
"TTCN_Logger::log_event_str(\"%s(): Decoded %s: \");\n"
"%s.log();\n"
"TTCN_Logger::end_event();\n"
"}\n", function_name, output_type->get_typename().c_str(), result_name);
if (prototype != PROTOTYPE_SLIDING) {
// checking for remaining data in the buffer if decoding was successful
str = mputprintf(str, "if (TTCN_EncDec::get_last_error_type() == "
"TTCN_EncDec::ET_NONE) {\n"
"if (ttcn_buffer.get_pos() < ttcn_buffer.get_len()) {\n"
"ttcn_buffer.cut();\n"
"OCTETSTRING tmp_os;\n"
"ttcn_buffer.get_string(tmp_os);\n"
"TTCN_Logger::begin_event_log2str();\n"
"%s.log();\n"
"CHARSTRING remaining_stream = TTCN_Logger::end_event_log2str();\n"
"TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_EXTRA_DATA, "
"\"%s(): Data remained at the end of the stream after successful "
"decoding: %%s\", (const char*) remaining_stream);\n"
"}\n",
(input_type->get_type_refd_last()->get_typetype_ttcn3() ==
Common::Type::T_BSTR) ? "oct2bit(tmp_os)" : "tmp_os", function_name);
// closing the block and returning the appropriate result or status code
if (prototype == PROTOTYPE_BACKTRACK) {
if (debugger_active) {
str = mputstr(str, "ttcn3_debugger.set_return_value(\"0\");\n");
}
str = mputstr(str,
"return 0;\n"
"} else {\n");
if (debugger_active) {
str = mputstr(str, "ttcn3_debugger.set_return_value(\"1\");\n");
}
str = mputstr(str,
"return 1;\n"
"}\n");
} else {
str = mputstr(str, "}\n");
if (prototype == PROTOTYPE_CONVERT) {
if (debugger_active) {
str = mputstr(str,
"ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), "
"ret_val.log(), TTCN_Logger::end_event_log2str()));\n");
}
str = mputstr(str, "return ret_val;\n");
}
}
} else {
// result handling and debug printout for sliding decoders
str = mputstr(str, "switch (TTCN_EncDec::get_last_error_type()) {\n"
"case TTCN_EncDec::ET_NONE: {\n"
// TTCN_Buffer::get_string will call OCTETSTRING::clean_up()
"ttcn_buffer.cut();\n");
if (input_type->get_type_refd_last()->get_typetype_ttcn3() ==
Common::Type::T_BSTR) {
str = mputprintf(str,
"OCTETSTRING tmp_os;\n"
"ttcn_buffer.get_string(tmp_os);\n"
"%s = oct2bit(tmp_os);\n", first_par_name);
}
else {
str = mputprintf(str, "ttcn_buffer.get_string(%s);\n", first_par_name);
}
str = mputprintf(str,
"if (TTCN_Logger::log_this_event(TTCN_Logger::DEBUG_ENCDEC)) {\n"
"TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);\n"
"TTCN_Logger::log_event_str(\"%s(): Stream after decoding: \");\n"
"%s.log();\n"
"TTCN_Logger::end_event();\n"
"}\n"
"%sreturn 0; }\n"
"case TTCN_EncDec::ET_INCOMPL_MSG:\n"
"case TTCN_EncDec::ET_LEN_ERR:\n"
"%sreturn 2;\n"
"default:\n"
"%sreturn 1;\n"
"}\n", function_name, first_par_name,
debugger_active ? "ttcn3_debugger.set_return_value(\"0\");\n" : "",
debugger_active ? "ttcn3_debugger.set_return_value(\"2\");\n" : "",
debugger_active ? "ttcn3_debugger.set_return_value(\"1\");\n" : "");
}
return str;
}
void Def_ExtFunction::generate_code(output_struct *target, bool)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
string return_type_name;
switch (asstype) {
case A_EXT_FUNCTION:
return_type_name = "void";
break;
case A_EXT_FUNCTION_RVAL:
return_type_name = return_type->get_genname_value(my_scope);
break;
case A_EXT_FUNCTION_RTEMP:
return_type_name = return_type->get_genname_template(my_scope);
break;
default:
FATAL_ERROR("Def_ExtFunction::generate_code()");
}
const char *return_type_str = return_type_name.c_str();
char *formal_par_list = fp_list->generate_code(memptystr(), fp_list->get_nof_fps());
fp_list->generate_code_defval(target);
bool in_class = my_scope->is_class_scope();
// function prototype
char*& header = in_class ? target->header.class_defs :
target->header.function_prototypes;
header = mputprintf(header, "%s %s %s(%s);\n",
in_class ? "virtual" : "extern", return_type_str, genname_str, formal_par_list);
if (function_type != EXTFUNC_MANUAL) {
// function body written by the compiler
char *body = 0;
#ifndef NDEBUG
body = mprintf("// written by %s in " __FILE__ " at %d\n"
, __FUNCTION__, __LINE__);
#endif
body = mputprintf(body,
"%s %s(%s)\n"
"{\n"
, return_type_str, genname_str, formal_par_list);
if (debugger_active) {
body = generate_code_debugger_function_init(body, this);
}
switch (function_type) {
case EXTFUNC_ENCODE:
body = generate_code_encode(body);
break;
case EXTFUNC_DECODE:
body = generate_code_decode(body);
break;
default:
FATAL_ERROR("Def_ExtFunction::generate_code()");
}
body = mputstr(body, "}\n\n");
target->source.function_bodies = mputstr(target->source.function_bodies,
body);
Free(body);
}
else if (in_class && my_scope->get_scope_class()->is_external()) {
char* out_par_str = enable_set_bound_out_param ? memptystr() :
fp_list->generate_code_set_unbound(memptystr());
target->source.methods = mputprintf(target->source.methods,
"%s %s::%s(%s)\n"
"{\n"
"%s\n"
"}\n\n", return_type_str,
my_scope->get_scope_class()->get_id()->get_name().c_str(),
genname_str, formal_par_list, out_par_str);
Free(out_par_str);
}
Free(formal_par_list);
if (!in_class) {
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s.add_function(\"%s\", (genericfunc_t)&%s, NULL);\n",
get_module_object_name(), id->get_dispname().c_str(), genname_str);
}
}
void Def_ExtFunction::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
void Def_ExtFunction::dump_internal(unsigned level) const
{
DEBUG(level, "External function: %s", id->get_dispname().c_str());
DEBUG(level + 1, "Parameters:");
fp_list->dump(level + 2);
if (return_type) {
DEBUG(level + 1, "Return type:");
return_type->dump(level + 2);
if(asstype == A_EXT_FUNCTION_RTEMP) DEBUG(level + 1, "Returns template");
}
DEBUG(level + 1, "Deterministic: %s", deterministic ? "true" : "false");
if (prototype != PROTOTYPE_NONE)
DEBUG(level + 1, "Prototype: %s", get_prototype_name());
if (function_type != EXTFUNC_MANUAL) {
DEBUG(level + 1, "Automatically generated: %s",
function_type == EXTFUNC_ENCODE ? "encoder" : "decoder");
DEBUG(level + 2, "Encoding type: %s",
Type::get_encoding_name(encoding_type));
if (encoding_options)
DEBUG(level + 2, "Encoding options: %s", encoding_options->c_str());
}
if (eb_list) eb_list->dump(level + 1);
if (exceptions != NULL) {
DEBUG(level + 1, "Exceptions:");
exceptions->dump(level + 2);
}
}
void Def_ExtFunction::generate_json_schema_ref(map<Type*, JSON_Tokenizer>& json_refs)
{
// only do anything if this is a JSON encoding or decoding function
if (encoding_type == Type::CT_JSON &&
(function_type == EXTFUNC_ENCODE || function_type == EXTFUNC_DECODE)) {
// retrieve the encoded type
Type* type = NULL;
if (function_type == EXTFUNC_ENCODE) {
// for encoding functions it's always the first parameter
type = fp_list->get_fp_byIndex(0)->get_Type();
} else {
// for decoding functions it depends on the prototype
switch (prototype) {
case PROTOTYPE_CONVERT:
type = return_type;
break;
case PROTOTYPE_FAST:
case PROTOTYPE_BACKTRACK:
case PROTOTYPE_SLIDING:
type = fp_list->get_fp_byIndex(1)->get_Type();
break;
default:
FATAL_ERROR("Def_ExtFunction::generate_json_schema_ref");
}
}
// step over the type reference created for this function
type = type->get_type_refd();
JSON_Tokenizer* json = NULL;
if (json_refs.has_key(type)) {
// the schema segment containing the type's reference already exists
json = json_refs[type];
} else {
// the schema segment doesn't exist yet, create it and insert the reference
json = new JSON_Tokenizer;
json_refs.add(type, json);
type->generate_json_schema_ref(*json);
}
// insert a property to specify which function this is (encoding or decoding)
json->put_next_token(JSON_TOKEN_NAME, (function_type == EXTFUNC_ENCODE) ?
"encoding" : "decoding");
// place the function's info in an object
json->put_next_token(JSON_TOKEN_OBJECT_START);
// insert information related to the function's prototype in an array
json->put_next_token(JSON_TOKEN_NAME, "prototype");
json->put_next_token(JSON_TOKEN_ARRAY_START);
// 1st element: external function prototype name (as string)
switch(prototype) {
case PROTOTYPE_CONVERT:
json->put_next_token(JSON_TOKEN_STRING, "\"convert\"");
break;
case PROTOTYPE_FAST:
json->put_next_token(JSON_TOKEN_STRING, "\"fast\"");
break;
case PROTOTYPE_BACKTRACK:
json->put_next_token(JSON_TOKEN_STRING, "\"backtrack\"");
break;
case PROTOTYPE_SLIDING:
json->put_next_token(JSON_TOKEN_STRING, "\"sliding\"");
break;
default:
FATAL_ERROR("Def_ExtFunction::generate_json_schema_ref");
}
// 2nd element: external function name
char* func_name_str = mprintf("\"%s\"", id->get_dispname().c_str());
json->put_next_token(JSON_TOKEN_STRING, func_name_str);
Free(func_name_str);
// the rest of the elements contain the names of the function's parameters (1 or 2)
for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
char* param_str = mprintf("\"%s\"",
fp_list->get_fp_byIndex(i)->get_id().get_dispname().c_str());
json->put_next_token(JSON_TOKEN_STRING, param_str);
Free(param_str);
}
// end of the prototype's array
json->put_next_token(JSON_TOKEN_ARRAY_END);
// insert error behavior data
if (eb_list != NULL) {
json->put_next_token(JSON_TOKEN_NAME, "errorBehavior");
json->put_next_token(JSON_TOKEN_OBJECT_START);
// add each error behavior modification as a property
for (size_t i = 0; i < eb_list->get_nof_ebs(); ++i) {
ErrorBehaviorSetting* eb = eb_list->get_ebs_byIndex(i);
json->put_next_token(JSON_TOKEN_NAME, eb->get_error_type().c_str());
char* handling_str = mprintf("\"%s\"", eb->get_error_handling().c_str());
json->put_next_token(JSON_TOKEN_STRING, handling_str);
Free(handling_str);
}
json->put_next_token(JSON_TOKEN_OBJECT_END);
}
// insert printing type
if (printing != NULL) {
json->put_next_token(JSON_TOKEN_NAME, "printing");
json->put_next_token(JSON_TOKEN_STRING,
(printing->get_printing() == PrintingType::PT_PRETTY) ?
"\"pretty\"" : "\"compact\"");
}
// end of this function's object
json->put_next_token(JSON_TOKEN_OBJECT_END);
}
}
// =================================
// ===== Def_AbsFunction
// =================================
Def_AbsFunction::~Def_AbsFunction()
{
// TODO
}
Definition* Def_AbsFunction::clone() const
{
return NULL;
// TODO
}
void Def_AbsFunction::chk()
{
if (checked) {
return;
}
checked = true;
Error_Context cntxt(this, "In abstract function definition `%s'",
id->get_dispname().c_str());
ClassTypeBody* my_class = my_scope->get_scope_class();
if (my_class == NULL) {
FATAL_ERROR("Def_AbsFunction::chk");
}
if (!my_class->is_abstract() && !my_class->is_trait()) {
error("Concrete class type `%s' cannot have abstract methods",
my_class->get_my_def()->get_Type()->get_typename().c_str());
}
fp_list->chk(asstype);
if (return_type != NULL) {
Error_Context cntxt2(return_type, "In return type");
return_type->chk();
return_type->chk_as_return_type(asstype == A_FUNCTION_RVAL,
"n abstract function");
}
if (exceptions != NULL) {
exceptions->chk(this);
}
if (!semantic_check_only) {
fp_list->set_genname(get_genname());
}
// TODO: with attributes
}
void Def_AbsFunction::generate_code(output_struct* target, bool)
{
const string& t_genname = get_genname();
const char* genname_str = t_genname.c_str();
string return_type_name;
switch (asstype) {
case A_FUNCTION:
return_type_name = "void";
break;
case A_FUNCTION_RVAL:
return_type_name = return_type->get_genname_value(my_scope);
break;
case A_FUNCTION_RTEMP:
return_type_name = return_type->get_genname_template(my_scope);
break;
default:
FATAL_ERROR("Def_AbsFunction::generate_code");
}
const char* return_type_str = return_type_name.c_str();
char* formal_par_list = fp_list->generate_code(memptystr(), fp_list->get_nof_fps());
fp_list->generate_code_defval(target);
target->header.class_defs = mputprintf(target->header.class_defs,
"virtual %s %s(%s) = 0;\n", return_type_str, genname_str, formal_par_list);
Free(formal_par_list);
}
// TODO
// =================================
// ===== Def_Altstep
// =================================
Def_Altstep::Def_Altstep(Identifier *p_id, FormalParList *p_fpl,
Reference *p_runs_on_ref, Reference *p_mtc_ref,
Reference *p_system_ref, StatementBlock *p_sb,
AltGuards *p_ags, Common::SignatureExceptions* p_exceptions)
: Definition(A_ALTSTEP, p_id), fp_list(p_fpl), runs_on_ref(p_runs_on_ref),
runs_on_type(0), mtc_ref(p_mtc_ref), mtc_type(0),
system_ref(p_system_ref), system_type(0), sb(p_sb), ags(p_ags), exceptions(p_exceptions)
{
if (!p_fpl || !p_sb || !p_ags)
FATAL_ERROR("Def_Altstep::Def_Altstep()");
fp_list->set_my_def(this);
sb->set_my_def(this);
ags->set_my_def(this);
ags->set_my_sb(sb, 0);
}
Def_Altstep::~Def_Altstep()
{
delete fp_list;
delete runs_on_ref;
delete mtc_ref;
delete system_ref;
delete sb;
delete ags;
delete exceptions;
}
Def_Altstep *Def_Altstep::clone() const
{
FATAL_ERROR("Def_Altstep::clone");
}
void Def_Altstep::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
fp_list->set_fullname(p_fullname + ".<formal_par_list>");
if (runs_on_ref) runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>");
if (mtc_ref) mtc_ref->set_fullname(p_fullname + ".<mtc_type>");
if (system_ref) system_ref->set_fullname(p_fullname + ".<system_type>");
sb->set_fullname(p_fullname+".<block>");
ags->set_fullname(p_fullname + ".<guards>");
if (exceptions != NULL) {
exceptions->set_fullname(p_fullname + ".<exceptions>");
}
}
void Def_Altstep::set_my_scope(Scope *p_scope)
{
bridgeScope.set_parent_scope(p_scope);
bridgeScope.set_scopeMacro_name(id->get_dispname());
Definition::set_my_scope(&bridgeScope);
// the scope of the parameter list is set during checking
if (runs_on_ref) runs_on_ref->set_my_scope(&bridgeScope);
if (mtc_ref) mtc_ref->set_my_scope(&bridgeScope);
if (system_ref) system_ref->set_my_scope(&bridgeScope);
sb->set_my_scope(fp_list);
ags->set_my_scope(sb);
if (exceptions != NULL) {
exceptions->set_my_scope(&bridgeScope);
}
}
Type *Def_Altstep::get_RunsOnType()
{
if (!checked) chk();
return runs_on_type;
}
Type *Def_Altstep::get_MtcType()
{
if (!checked) chk();
return mtc_type;
}
Type *Def_Altstep::get_SystemType()
{
if (!checked) chk();
return system_type;
}
FormalParList *Def_Altstep::get_FormalParList()
{
if (!checked) chk();
return fp_list;
}
RunsOnScope *Def_Altstep::get_runs_on_scope(Type *comptype)
{
Module *my_module = dynamic_cast<Module*>(my_scope->get_scope_mod());
if (!my_module) FATAL_ERROR("Def_Altstep::get_runs_on_scope()");
return my_module->get_runs_on_scope(comptype);
}
void Def_Altstep::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In altstep definition `%s'",
id->get_dispname().c_str());
Scope *parlist_scope = my_scope;
if (runs_on_ref) {
Error_Context cntxt2(runs_on_ref, "In `runs on' clause");
runs_on_type = runs_on_ref->chk_comptype_ref();
if (runs_on_type) {
Scope *runs_on_scope = get_runs_on_scope(runs_on_type);
runs_on_scope->set_parent_scope(my_scope);
parlist_scope = runs_on_scope;
}
}
if (mtc_ref) {
Error_Context cntxt2(mtc_ref, "In `mtc' clause");
mtc_type = mtc_ref->chk_comptype_ref();
}
if (system_ref) {
Error_Context cntxt2(system_ref, "In `system' clause");
system_type = system_ref->chk_comptype_ref();
}
fp_list->set_my_scope(parlist_scope);
fp_list->chk(asstype);
sb->chk();
ags->set_is_altstep();
ags->set_my_ags(ags);
ags->set_my_laic_stmt(ags, 0);
ags->chk();
if (!semantic_check_only) {
fp_list->set_genname(get_genname());
sb->set_code_section(GovernedSimple::CS_INLINE);
ags->set_code_section(GovernedSimple::CS_INLINE);
}
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
if (exceptions != NULL) {
exceptions->chk(this);
}
}
void Def_Altstep::generate_code(output_struct *target, bool)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const char *dispname_str = id->get_dispname().c_str();
// function for altstep instance:
// assemble the function body first (this also determines which parameters
// are never used)
char* body = create_location_object(memptystr(), "ALTSTEP", dispname_str);
if (debugger_active) {
body = generate_code_debugger_function_init(body, this);
}
body = sb->generate_code(body, target->header.global_vars,
target->source.global_vars, ags);
// generate a smart formal parameter list (omits unused parameter names)
char *formal_par_list = fp_list->generate_code(memptystr());
fp_list->generate_code_defval(target);
// function for altstep instance: prototype
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern alt_status %s_instance(%s);\n", genname_str, formal_par_list);
// generate shadow objects for parameters if needed
// (this needs be done after the body is generated, so it to knows which
// parameters are never used)
char* shadow_objects = fp_list->generate_shadow_objects(memptystr());
// function for altstep instance: body
target->source.function_bodies = mputprintf(target->source.function_bodies,
"alt_status %s_instance(%s)\n"
"{\n"
"%s%s"
"}\n\n", genname_str, formal_par_list, shadow_objects, body);
Free(formal_par_list);
Free(shadow_objects);
Free(body);
char *actual_par_list =
fp_list->generate_code_actual_parlist(memptystr(), "");
// use a full formal parameter list for the rest of the functions
char *full_formal_par_list = fp_list->generate_code(memptystr(),
fp_list->get_nof_fps());
// wrapper function for stand-alone instantiation: prototype
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern void %s(%s);\n", genname_str, full_formal_par_list);
// wrapper function for stand-alone instantiation: body
target->source.function_bodies =
mputprintf(target->source.function_bodies, "void %s(%s)\n"
"{\n"
"altstep_begin:\n"
"boolean block_flag = FALSE;\n"
"alt_status altstep_flag = ALT_UNCHECKED, "
"default_flag = ALT_UNCHECKED;\n"
"for ( ; ; ) {\n"
"TTCN_Snapshot::take_new(block_flag);\n"
"if (altstep_flag != ALT_NO) {\n"
"altstep_flag = %s_instance(%s);\n"
"if (altstep_flag == ALT_YES || altstep_flag == ALT_BREAK) return;\n"
"else if (altstep_flag == ALT_REPEAT) goto altstep_begin;\n"
"}\n"
"if (default_flag != ALT_NO) {\n"
"default_flag = TTCN_Default::try_altsteps();\n"
"if (default_flag == ALT_YES || default_flag == ALT_BREAK) return;\n"
"else if (default_flag == ALT_REPEAT) goto altstep_begin;\n"
"}\n"
"if (altstep_flag == ALT_NO && default_flag == ALT_NO) "
"TTCN_error(\"None of the branches can be chosen in altstep %s.\");\n"
"else block_flag = TRUE;\n"
"}\n"
"}\n\n", genname_str, full_formal_par_list, genname_str, actual_par_list,
dispname_str);
// class for keeping the altstep in the default context
// the class is for internal use, we do not need to publish it in the
// header file
char* str = mprintf("class %s_Default : public Default_Base {\n", genname_str);
str = fp_list->generate_code_object(str, "par_");
str = mputprintf(str, "public:\n"
"%s_Default(%s);\n"
"alt_status call_altstep();\n"
"};\n\n", genname_str, full_formal_par_list);
target->source.class_defs = mputstr(target->source.class_defs, str);
Free(str);
// member functions of the class
str = mprintf("%s_Default::%s_Default(%s)\n"
" : Default_Base(\"%s\")", genname_str, genname_str, full_formal_par_list,
dispname_str);
for (size_t i = 0; i < fp_list->get_nof_fps(); i++) {
const char *fp_name_str =
fp_list->get_fp_byIndex(i)->get_id().get_name().c_str();
str = mputprintf(str, ", par_%s(%s)", fp_name_str, fp_name_str);
}
str = mputstr(str, "\n{\n}\n\n");
char *actual_par_list_prefixed =
fp_list->generate_code_actual_parlist(memptystr(), "par_");
str = mputprintf(str, "alt_status %s_Default::call_altstep()\n"
"{\n"
"return %s_instance(%s);\n"
"}\n\n", genname_str, genname_str, actual_par_list_prefixed);
Free(actual_par_list_prefixed);
target->source.methods = mputstr(target->source.methods, str);
Free(str);
// function for default activation: prototype
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern Default_Base *activate_%s(%s);\n", genname_str,
full_formal_par_list);
// function for default activation: body
str = mprintf("Default_Base *activate_%s(%s)\n"
"{\n", genname_str, full_formal_par_list);
str = mputprintf(str, "return new %s_Default(%s);\n"
"}\n\n", genname_str, actual_par_list);
target->source.function_bodies = mputstr(target->source.function_bodies,
str);
Free(str);
Free(full_formal_par_list);
Free(actual_par_list);
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s.add_altstep(\"%s\", (genericfunc_t)&%s_instance, (genericfunc_t )&activate_%s, "
"(genericfunc_t )&%s);\n", get_module_object_name(), dispname_str, genname_str,
genname_str, genname_str);
}
void Def_Altstep::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
void Def_Altstep::dump_internal(unsigned level) const
{
DEBUG(level, "Altstep: %s", id->get_dispname().c_str());
DEBUG(level + 1, "Parameters:");
fp_list->dump(level + 1);
if (runs_on_ref) {
DEBUG(level + 1, "Runs on clause:");
runs_on_ref->dump(level + 2);
}
if (mtc_ref) {
DEBUG(level + 1, "Mtc clause:");
mtc_ref->dump(level + 2);
}
if (system_ref) {
DEBUG(level + 1, "System clause:");
system_ref->dump(level + 2);
}
if (exceptions != NULL) {
DEBUG(level + 1, "Exceptions:");
exceptions->dump(level + 2);
}
/*
DEBUG(level + 1, "Local definitions:");
sb->dump(level + 2);
*/
DEBUG(level + 1, "Guards:");
ags->dump(level + 2);
}
void Def_Altstep::set_parent_path(WithAttribPath* p_path) {
Definition::set_parent_path(p_path);
sb->set_parent_path(w_attrib_path);
}
// =================================
// ===== Def_Testcase
// =================================
Def_Testcase::Def_Testcase(Identifier *p_id, FormalParList *p_fpl,
Reference *p_runs_on_ref, Reference *p_system_ref,
StatementBlock *p_block)
: Definition(A_TESTCASE, p_id), fp_list(p_fpl), runs_on_ref(p_runs_on_ref),
runs_on_type(0), system_ref(p_system_ref), system_type(0), block(p_block)
{
if (!p_fpl || !p_runs_on_ref || !p_block)
FATAL_ERROR("Def_Testcase::Def_Testcase()");
fp_list->set_my_def(this);
block->set_my_def(this);
}
Def_Testcase::~Def_Testcase()
{
delete fp_list;
delete runs_on_ref;
delete system_ref;
delete block;
}
Def_Testcase *Def_Testcase::clone() const
{
FATAL_ERROR("Def_Testcase::clone");
}
void Def_Testcase::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
fp_list->set_fullname(p_fullname + ".<formal_par_list>");
runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>");
if (system_ref) system_ref->set_fullname(p_fullname + ".<system_type>");
block->set_fullname(p_fullname + ".<statement_block>");
}
void Def_Testcase::set_my_scope(Scope *p_scope)
{
bridgeScope.set_parent_scope(p_scope);
bridgeScope.set_scopeMacro_name(id->get_dispname());
Definition::set_my_scope(&bridgeScope);
// the scope of the parameter list is set during checking
runs_on_ref->set_my_scope(&bridgeScope);
if (system_ref) system_ref->set_my_scope(&bridgeScope);
block->set_my_scope(fp_list);
}
Type *Def_Testcase::get_RunsOnType()
{
if (!checked) chk();
return runs_on_type;
}
Type *Def_Testcase::get_SystemType()
{
if (!checked) chk();
return system_type;
}
FormalParList *Def_Testcase::get_FormalParList()
{
if (!checked) chk();
return fp_list;
}
RunsOnScope *Def_Testcase::get_runs_on_scope(Type *comptype)
{
Module *my_module = dynamic_cast<Module*>(my_scope->get_scope_mod());
if (!my_module) FATAL_ERROR("Def_Testcase::get_runs_on_scope()");
return my_module->get_runs_on_scope(comptype);
}
void Def_Testcase::chk()
{
if (checked) return;
checked = true;
Error_Context cntxt(this, "In testcase definition `%s'",
id->get_dispname().c_str());
Scope *parlist_scope = my_scope;
{
Error_Context cntxt2(runs_on_ref, "In `runs on' clause");
runs_on_type = runs_on_ref->chk_comptype_ref();
if (runs_on_type) {
Scope *runs_on_scope = get_runs_on_scope(runs_on_type);
runs_on_scope->set_parent_scope(my_scope);
parlist_scope = runs_on_scope;
}
}
if (system_ref) {
Error_Context cntxt2(system_ref, "In `system' clause");
system_type = system_ref->chk_comptype_ref();;
}
fp_list->set_my_scope(parlist_scope);
fp_list->chk(asstype);
block->chk();
if (!semantic_check_only) {
fp_list->set_genname(get_genname());
block->set_code_section(GovernedSimple::CS_INLINE);
}
if (w_attrib_path) {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
}
void Def_Testcase::generate_code(output_struct *target, bool)
{
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const char *dispname_str = id->get_dispname().c_str();
// assemble the function body first (this also determines which parameters
// are never used)
// Checking whether the testcase was invoked from another one.
// At this point the location information should refer to the execute()
// statement rather than this testcase.
char* body = mputstr(memptystr(), "TTCN_Runtime::check_begin_testcase(has_timer, "
"timer_value);\n");
body = create_location_object(body, "TESTCASE", dispname_str);
body = fp_list->generate_shadow_objects(body);
if (debugger_active) {
body = generate_code_debugger_function_init(body, this);
}
body = mputprintf(body, "try {\n"
"%s"
"TTCN_Runtime::begin_testcase(\"%s\", \"%s\", ",
oop_features ? "try {\n" : "",
my_scope->get_scope_mod()->get_modid().get_dispname().c_str(),
dispname_str);
ComponentTypeBody *runs_on_body = runs_on_type->get_CompBody();
body = runs_on_body->generate_code_comptype_name(body);
body = mputstr(body, ", ");
if (system_type)
body = system_type->get_CompBody()->generate_code_comptype_name(body);
else body = runs_on_body->generate_code_comptype_name(body);
body = mputstr(body, ", has_timer, timer_value);\n");
body = block->generate_code(body, target->header.global_vars,
target->source.global_vars);
body = mputprintf(body,
"%s"
"} catch (const TC_Error& tc_error) {\n"
"} catch (const TC_End& tc_end) {\n"
"TTCN_Logger::log_str(TTCN_FUNCTION, \"Test case %s was stopped.\");\n"
"}\n",
oop_features ? "} catch (const EXCEPTION_BASE& exc) {\n"
"TTCN_error(\"Unhandled exception: %s\", (const char*) exc.get_log());\n"
"}\n" : "", dispname_str);
body = mputstr(body, "return TTCN_Runtime::end_testcase();\n");
// smart formal parameter list (names of unused parameters are omitted)
char *formal_par_list = fp_list->generate_code(memptystr());
fp_list->generate_code_defval(target);
if (fp_list->get_nof_fps() > 0)
formal_par_list = mputstr(formal_par_list, ", ");
formal_par_list = mputstr(formal_par_list,
"boolean has_timer, double timer_value");
// function prototype
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern verdicttype testcase_%s(%s);\n", genname_str, formal_par_list);
// function body
target->source.function_bodies = mputprintf(target->source.function_bodies,
"verdicttype testcase_%s(%s)\n"
"{\n"
"%s"
"}\n\n", genname_str, formal_par_list, body);
Free(formal_par_list);
Free(body);
if (fp_list->get_nof_fps() == 0) {
// adding to the list of startable testcases
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s.add_testcase_nonpard(\"%s\", testcase_%s);\n",
get_module_object_name(), dispname_str, genname_str);
} else {
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s.add_testcase_pard(\"%s\", (genericfunc_t)&testcase_%s);\n",
get_module_object_name(), dispname_str, genname_str);
// If every formal parameter has a default value, the testcase
// might be callable after all.
bool callable = true;
for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
FormalPar *fp = fp_list->get_fp_byIndex(i);
if (!fp->has_defval()) {
callable = false;
break;
}
}
if (callable) {
// Write a wrapper, which acts as a no-param testcase
// by calling the parameterized testcase with the default values.
target->header.function_prototypes =
mputprintf(target->header.function_prototypes,
"extern verdicttype testcase_%s_defparams(boolean has_timer, double timer_value);\n",
genname_str);
expression_struct expr;
Code::init_expr(&expr);
target->source.function_bodies = mputprintf(target->source.function_bodies,
"verdicttype testcase_%s_defparams(boolean has_timer, double timer_value) {\n",
genname_str);
expr.expr = mprintf(" return testcase_%s(", genname_str);
for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
FormalPar *fp = fp_list->get_fp_byIndex(i);
ActualPar *ap = fp->get_defval();
param_eval_t param_eval = fp != NULL ? fp->get_eval_type() : NORMAL_EVAL;
switch (ap->get_selection()) {
case ActualPar::AP_VALUE:
if (param_eval != NORMAL_EVAL) {
LazyFuzzyParamData::init(false);
LazyFuzzyParamData::generate_code(&expr, ap->get_Value(), my_scope,
param_eval == LAZY_EVAL);
LazyFuzzyParamData::clean();
}
else {
expr.expr = mputstr(expr.expr,
ap->get_Value()->get_genname_own(my_scope).c_str());
}
break;
case ActualPar::AP_TEMPLATE:
if (param_eval != NORMAL_EVAL) {
LazyFuzzyParamData::init(false);
LazyFuzzyParamData::generate_code(&expr, ap->get_TemplateInstance(),
ap->get_gen_restriction_check(), my_scope, param_eval == LAZY_EVAL);
LazyFuzzyParamData::clean();
}
else {
if (use_runtime_2 && fp->get_defpar_wrapper() == Common::Type::NO_DEFPAR_WRAPPER) {
string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
expr.preamble = mputprintf(expr.preamble, "%s %s;\n",
fp->get_Type()->get_genname_template(my_scope).c_str(), tmp_id.c_str());
// use the actual parameter's scope, not the formal parameter's, when
// generating the template's initialization code
ap->get_TemplateInstance()->set_my_scope(my_scope);
expr.preamble = FormalPar::generate_code_defval_template(expr.preamble,
ap->get_TemplateInstance(), tmp_id, ap->get_gen_restriction_check());
expr.expr = mputstr(expr.expr, tmp_id.c_str());
}
else {
expr.expr = mputstr(expr.expr,
ap->get_TemplateInstance()->get_Template()->get_genname_own(my_scope).c_str());
}
}
break;
case ActualPar::AP_REF:
expr.expr = mputstr(expr.expr,
ap->get_Ref()->get_refd_assignment()->get_genname_from_scope(my_scope).c_str());
break;
case ActualPar::AP_DEFAULT:
// Can't happen. This ActualPar was created by
// Ttcn::FormalPar::chk_actual_par as the default value for
// a FormalPar, and it only ever creates vale, template or ref.
// no break
default:
FATAL_ERROR("Def_Testcase::generate_code()");
}
// always append a comma, because has_timer and timer_value follows
expr.expr = mputstrn(expr.expr, ", ", 2);
}
// the expression_struct merging algorithm adds would add an extra block,
// so just append the preamble and expr fields manually
target->source.function_bodies = mputprintf(target->source.function_bodies,
"%s%shas_timer, timer_value);\n"
"}\n\n", expr.preamble == NULL ? "" : expr.preamble, expr.expr);
Code::free_expr(&expr);
// Add the non-parameterized wrapper *after* the parameterized one,
// with the same name. Linear search will always find the first
// (user-visible, parameterized) testcase.
// TTCN_Module::execute_testcase knows that if after a parameterized
// testcase another testcase with the same name follows,
// it's the callable, non-parameterized wrapper.
//
// TTCN_Module::list_testcases skips parameterized testcases;
// it will now list the non-parameterized wrapper.
target->functions.pre_init = mputprintf(target->functions.pre_init,
"%s.add_testcase_nonpard(\"%s\", testcase_%s_defparams);\n",
get_module_object_name(), dispname_str, genname_str);
}
} // has formal parameters
}
void Def_Testcase::generate_code(CodeGenHelper& cgh) {
generate_code(cgh.get_current_outputstruct());
}
void Def_Testcase::dump_internal(unsigned level) const
{
DEBUG(level, "Testcase: %s", id->get_dispname().c_str());
DEBUG(level + 1, "Parameters:");
fp_list->dump(level + 1);
DEBUG(level + 1, "Runs on clause:");
runs_on_ref->dump(level + 2);
if (system_ref) {
DEBUG(level + 1, "System clause:");
system_ref->dump(level + 2);
}
DEBUG(level + 1, "Statement block:");
block->dump(level + 2);
}
void Def_Testcase::set_parent_path(WithAttribPath* p_path) {
Definition::set_parent_path(p_path);
if (block)
block->set_parent_path(w_attrib_path);
}
// =================================
// ===== Def_Constructor
// =================================
Def_Constructor::Def_Constructor(FormalParList* p_fp_list,
Reference* p_base_call, StatementBlock* p_block)
: Definition(A_CONSTRUCTOR, new Common::Identifier(
Common::Identifier::ID_TTCN, string("create"), true)),
fp_list(p_fp_list), base_call(p_base_call), block(p_block)
{
if (p_fp_list == NULL) {
FATAL_ERROR("Def_Constructor::Def_Constructor");
}
fp_list->set_my_def(this);
if (block != NULL) {
block->set_my_def(this);
}
}
Def_Constructor::~Def_Constructor()
{
delete fp_list;
delete base_call;
delete block;
for (size_t i = 0; i < uninit_members.size(); ++i) {
delete uninit_members.get_nth_elem(i);
}
uninit_members.clear();
}
Def_Constructor* Def_Constructor::clone() const
{
FATAL_ERROR("Def_Constructor::clone");
}
void Def_Constructor::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
fp_list->set_fullname(p_fullname + ".<formal_par_list>");
if (base_call != NULL) {
base_call->set_fullname(p_fullname + ".<base_call>");
}
if (block != NULL) {
block->set_fullname(p_fullname + ".<statement_block>");
}
}
void Def_Constructor::set_my_scope(Scope* p_scope)
{
bridgeScope.set_parent_scope(p_scope);
bridgeScope.set_scopeMacro_name(id->get_dispname());
Definition::set_my_scope(&bridgeScope);
fp_list->set_my_scope(&bridgeScope);
if (base_call != NULL) {
base_call->set_my_scope(fp_list);
}
if (block != NULL) {
block->set_my_scope(fp_list);
}
}
FormalParList* Def_Constructor::get_FormalParList()
{
if (!checked) chk();
return fp_list;
}
void Def_Constructor::add_uninit_member(const Identifier* p_member_id, bool p_is_template)
{
uninit_members.add(p_member_id, new bool(p_is_template));
}
void Def_Constructor::chk()
{
if (checked) {
return;
}
checked = true;
Error_Context cntxt(this, "In constructor definition");
ClassTypeBody* my_class = my_scope->get_scope_class();
fp_list->chk(asstype);
for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
FormalPar* fp = fp_list->get_fp_byIndex(i);
if (fp->has_defval()) {
fp->get_defval()->chk_ctor_defpar(my_class->has_default_constructor(), false);
}
}
ClassTypeBody* base_class = my_class->get_base_class();
if (base_call != NULL) {
Error_Context cntxt2(this, "In super-constructor call");
if (base_class == NULL) {
base_call->error("Class type `%s' does not have a superclass",
my_class->get_my_def()->get_Type()->get_typename().c_str());
}
else {
Common::Assignment* base_call_ass = base_call->get_refd_assignment(true);
if (base_call_ass != NULL) {
if (base_call_ass->get_asstype() != Common::Assignment::A_CONSTRUCTOR) {
base_call->error("Reference to constructor was expected instead of %s",
base_call_ass->get_assname());
}
else {
ClassTypeBody* base_call_class = base_call_ass->get_my_scope()->get_scope_class();
if (base_call_class != base_class) {
base_call->error("expected call to constructor of class type `%s', "
"instead of class type `%s'",
my_class->get_base_type()->get_typename().c_str(),
base_call_class->get_my_def()->get_Type()->get_typename().c_str());
}
else if (base_call->has_parameters()) {
base_call->get_parlist()->chk_ctor_defpar(my_class->has_default_constructor(), true);
}
}
}
}
}
else if (base_class != NULL && !my_class->is_external()) {
Def_Constructor* base_constructor = base_class->get_constructor();
if (base_constructor != NULL &&
!base_constructor->get_FormalParList()->has_only_default_values()) {
error("Missing super-constructor call");
}
}
// reset 'usage found' flag for the constructor's formal parameters with default values
// (usage in the base constructor call doesn't count for these)
for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) {
FormalPar* fp = fp_list->get_fp_byIndex(i);
if (fp->has_defval()) {
fp->reset_usage_found();
}
}
if (block != NULL) {
if (my_class->is_external()) {
error("The constructor of an external class cannot have a body");
}
block->chk();
}
else if (!my_class->is_external()) {
error("Missing constructor body");
}
if (!semantic_check_only) {
// prefix 'create' with the class name when forming parameter names
// to avoid collisions in the generated code
fp_list->set_genname(my_scope->get_scope_class()->get_id()->get_name() +
string("_") + get_genname());
if (block != NULL) {
block->set_code_section(GovernedSimple::CS_INLINE);
}
}
}
void Def_Constructor::generate_code(output_struct *target, bool clean_up)
{
if (!enable_set_bound_out_param) {
target->temp.constructor_block = fp_list->generate_code_set_unbound(
target->temp.constructor_block);
}
fp_list->generate_code_defval(target);
target->temp.constructor_block =
fp_list->generate_code_defpar_init(memptystr());
char* block_gen_str = block != NULL ? block->generate_code(memptystr(),
target->header.global_vars, target->source.global_vars) : NULL;
if (block != NULL) {
target->temp.constructor_block = fp_list->generate_shadow_objects(
target->temp.constructor_block);
target->temp.constructor_block = mputstr(target->temp.constructor_block, block_gen_str);
for (size_t i = 0; i < uninit_members.size(); ++i) {
const Identifier* member_id = uninit_members.get_nth_key(i);
bool is_template = *uninit_members.get_nth_elem(i);
target->temp.constructor_block = mputprintf(target->temp.constructor_block,
"if (!%s.is_bound()) TTCN_error(\"%s member `%s' is not initialized "
"by the end of the constructor's execution.\");\n",
member_id->get_name().c_str(), is_template ? "Template" : "Constant",
member_id->get_dispname().c_str());
}
}
Free(block_gen_str);
}
void Def_Constructor::set_parent_path(WithAttribPath* p_path)
{
Definition::set_parent_path(p_path);
if (block != NULL) {
block->set_parent_path(w_attrib_path);
}
}
// =================================
// ===== FormalPar
// =================================
FormalPar::FormalPar(asstype_t p_asstype, Type *p_type, Identifier* p_name,
TemplateInstance *p_defval, param_eval_t p_eval)
: Definition(p_asstype, p_name), type(p_type), my_parlist(0),
used_as_lvalue(false), template_restriction(TR_NONE),
eval(p_eval), defval_generated(false), usage_found(false)
{
switch (p_asstype) {
case A_PAR_VAL:
case A_PAR_VAL_IN:
case A_PAR_VAL_OUT:
case A_PAR_VAL_INOUT:
case A_PAR_TEMPL_IN:
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
case A_PAR_PORT:
break;
default:
FATAL_ERROR("Ttcn::FormalPar::FormalPar(): invalid parameter type");
}
if (!p_type)
FATAL_ERROR("Ttcn::FormalPar::FormalPar(): NULL pointer");
type->set_ownertype(Type::OT_FORMAL_PAR, this);
defval.ti = p_defval;
}
FormalPar::FormalPar(asstype_t p_asstype,
template_restriction_t p_template_restriction, Type *p_type,
Identifier* p_name, TemplateInstance *p_defval, param_eval_t p_eval)
: Definition(p_asstype, p_name), type(p_type), my_parlist(0),
used_as_lvalue(false), template_restriction(p_template_restriction),
eval(p_eval), defval_generated(false), usage_found(false)
{
switch (p_asstype) {
case A_PAR_TEMPL_IN:
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
break;
default:
FATAL_ERROR("Ttcn::FormalPar::FormalPar(): parameter not template");
}
if (!p_type)
FATAL_ERROR("Ttcn::FormalPar::FormalPar(): NULL pointer");
type->set_ownertype(Type::OT_FORMAL_PAR, this);
defval.ti = p_defval;
}
FormalPar::FormalPar(asstype_t p_asstype, Identifier* p_name,
TemplateInstance *p_defval)
: Definition(p_asstype, p_name), type(0), my_parlist(0),
used_as_lvalue(false), template_restriction(TR_NONE), eval(NORMAL_EVAL),
defval_generated(false), usage_found(false)
{
if (p_asstype != A_PAR_TIMER)
FATAL_ERROR("Ttcn::FormalPar::FormalPar(): invalid parameter type");
defval.ti = p_defval;
}
FormalPar::FormalPar(const FormalPar& p)
: Definition(p.asstype, p.id->clone()), type(p.type->clone()), my_parlist(0),
used_as_lvalue(false), template_restriction(p.template_restriction),
eval(p.eval), defval_generated(false), usage_found(false)
{
type->set_ownertype(Type::OT_FORMAL_PAR, this);
checked = p.checked;
if (checked) {
defval.ap = p.defval.ap != NULL ? p.defval.ap->clone() : NULL;
}
else {
defval.ti = p.defval.ti != NULL ? p.defval.ti->clone() : NULL;
}
}
FormalPar::~FormalPar()
{
delete type;
if (checked) delete defval.ap;
else delete defval.ti;
}
FormalPar* FormalPar::clone() const
{
return new FormalPar(*this);
}
void FormalPar::set_fullname(const string& p_fullname)
{
Definition::set_fullname(p_fullname);
if (type) type->set_fullname(p_fullname + ".<type>");
if (checked) {
if (defval.ap) defval.ap->set_fullname(p_fullname + ".<default_value>");
} else {
if (defval.ti) defval.ti->set_fullname(p_fullname + ".<default_value>");
}
}
void FormalPar::set_my_scope(Scope *p_scope)
{
Definition::set_my_scope(p_scope);
if (type) type->set_my_scope(p_scope);
if (checked) {
if (defval.ap) defval.ap->set_my_scope(p_scope);
} else {
if (defval.ti) defval.ti->set_my_scope(p_scope);
}
}
bool FormalPar::is_local() const
{
return true;
}
Type *FormalPar::get_Type()
{
if (!checked) chk();
if (!type) FATAL_ERROR("FormalPar::get_Type()");
return type;
}
void FormalPar::chk()
{
if (checked) return;
TemplateInstance *default_value = defval.ti;
defval.ti = 0;
if (type) {
type->chk();
Type *t = type->get_type_refd_last();
// checks for forbidden type <-> parameter combinations
switch (t->get_typetype()) {
case Type::T_PORT:
switch (asstype) {
case A_PAR_VAL:
case A_PAR_VAL_INOUT:
asstype = A_PAR_PORT;
break;
case A_PAR_PORT:
break; // should only happen in recursive calls
default:
error("Port type `%s' cannot be used as %s",
t->get_fullname().c_str(), get_assname());
}
break;
case Type::T_SIGNATURE:
switch (asstype) {
case A_PAR_TEMPL_IN:
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
break;
default:
error("Signature `%s' cannot be used as %s",
t->get_fullname().c_str(), get_assname());
}
break;
default:
switch (asstype) {
case A_PAR_PORT:
case A_PAR_TIMER:
FATAL_ERROR("FormalPar::chk()");
case A_PAR_VAL:
asstype = A_PAR_VAL_IN;
default:
break;
}
}
} else if (asstype != A_PAR_TIMER) FATAL_ERROR("FormalPar::chk()");
checked = true;
if (default_value) {
Error_Context cntxt(default_value, "In default value");
Definition* def = my_parlist->get_my_def();
Scope* scope = def != NULL ? def->get_my_scope() : NULL;
if (scope != NULL && scope->is_class_scope()) {
switch (asstype) {
case A_PAR_VAL_IN:
case A_PAR_TEMPL_IN:
scope->get_scope_class()->add_defpar(this);
break;
default:
default_value->error("`out' and `inout' parameters of class functions cannot have default values");
break;
}
}
defval.ap = chk_actual_par(default_value, Type::EXPECTED_STATIC_VALUE);
delete default_value;
if (!semantic_check_only)
defval.ap->set_code_section(GovernedSimple::CS_POST_INIT);
}
switch (asstype) {
case A_PAR_VAL_IN:
case A_PAR_TEMPL_IN:
if (eval == NORMAL_EVAL &&
// altstep 'in' parameters are always shadowed in RT2, because if a default
// altstep deactivates itself, then its parameters are deleted;
// update the genname so that all references in the generated code
// will point to the shadow object
((use_runtime_2 && my_parlist->get_my_def() != NULL &&
my_parlist->get_my_def()->get_asstype() == Definition::A_ALTSTEP) ||
// always shadow 'in' parameters of class type (to avoid having to deal
// with constant OBJECT_REFs at runtime)
(type != NULL && type->get_type_refd_last()->get_typetype() == Type::T_CLASS))) {
use_as_lvalue(*this);
}
break;
default:
break;
}
}
bool FormalPar::has_defval() const
{
if (checked) return has_defval_checked();
else return defval.ti != 0;
}
bool FormalPar::has_defval_checked() const
{
return checked && defval.ap != 0;
}
bool FormalPar::has_notused_defval() const
{
if (checked) FATAL_ERROR("FormalPar::has_notused_defval");
if (!defval.ti || !defval.ti->get_Template())
return false;
return defval.ti->get_Template()->get_templatetype()
== Template::TEMPLATE_NOTUSED;
}
ActualPar *FormalPar::get_defval() const
{
if (!checked) FATAL_ERROR("FormalPar::get_defval()");
return defval.ap;
}
// Extract the TemplateInstance from an ActualPar.
void FormalPar::set_defval(ActualPar *defpar)
{
// ActualPar::clone() is not implemented, since we need such a function
// only here only for AP_{VALUE,TEMPLATE} parameters. AP_ERROR can also
// happen for Def_Template nodes, but they will be errors later.
// FIXME: This function is Def_Template specific.
if (!defval.ti->get_Template() || defval.ti->get_Template()
->get_templatetype() != Template::TEMPLATE_NOTUSED)
FATAL_ERROR("FormalPar::set_defval()");
TemplateInstance *reversed_ti = 0;
switch (defpar->get_selection()) {
case ActualPar::AP_VALUE:
reversed_ti = new TemplateInstance(type->clone(), 0, new Template
(defpar->get_Value()->clone())); // Trust the clone().
break;
case ActualPar::AP_TEMPLATE:
reversed_ti = defpar->get_TemplateInstance()->clone();
break;
case ActualPar::AP_ERROR:
break; // Can happen, but let it go.
case ActualPar::AP_REF:
case ActualPar::AP_DEFAULT:
default:
FATAL_ERROR("FormalPar::set_defval()");
}
if (reversed_ti) {
delete defval.ti;
reversed_ti->set_my_scope(get_my_scope());
defval.ti = reversed_ti;
}
}
ActualPar *FormalPar::chk_actual_par(TemplateInstance *actual_par,
Type::expected_value_t exp_val)
{
if (!checked) chk();
switch (asstype) {
case A_PAR_VAL:
case A_PAR_VAL_IN:
return chk_actual_par_value(actual_par, exp_val);
case A_PAR_VAL_OUT:
case A_PAR_VAL_INOUT:
return chk_actual_par_by_ref(actual_par, false, exp_val);
case A_PAR_TEMPL_IN:
return chk_actual_par_template(actual_par, exp_val);
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
return chk_actual_par_by_ref(actual_par, true, exp_val);
case A_PAR_TIMER:
return chk_actual_par_timer(actual_par, exp_val);
case A_PAR_PORT:
return chk_actual_par_port(actual_par, exp_val);
default:
FATAL_ERROR("FormalPar::chk_actual_par()");
}
return 0; // to avoid warnings
}
ActualPar *FormalPar::chk_actual_par_value(TemplateInstance *actual_par,
Type::expected_value_t exp_val)
{
actual_par->chk_Type(type);
Reference *derived_ref = actual_par->get_DerivedRef();
if (derived_ref) {
derived_ref->error("An in-line modified template cannot be used as %s",
get_assname());
actual_par->chk_DerivedRef(type);
}
Template *ap_template = actual_par->get_Template();
if (ap_template->is_Value()) {
Value *v = ap_template->get_Value();
v->set_my_governor(type);
type->chk_this_value_ref(v);
type->chk_this_value(v, 0, exp_val, WARNING_FOR_INCOMPLETE,
OMIT_NOT_ALLOWED, SUB_CHK);
return new ActualPar(v);
} else {
actual_par->error("A specific value without matching symbols "
"was expected for a %s", get_assname());
return new ActualPar();
}
}
static void chk_defpar_value(const Value* v)
{
Common::Reference *vref = v->get_reference();
Common::Assignment *ass2 = vref->get_refd_assignment();
ass2->chk();
Scope *scope = ass2->get_my_scope();
ComponentTypeBody *ctb = dynamic_cast<ComponentTypeBody *>(scope);
if (ctb) { // this is a component variable
v->error("default value cannot refer to"
" a template field of the component in the `runs on' clause");
}
}
static void chk_defpar_template(const Template *body,
Type::expected_value_t exp_val)
{
switch (body->get_templatetype()) {
case Template::TEMPLATE_ERROR:
break; // could be erroneous in the source; skip it
case Template::TEMPLATE_NOTUSED:
case Template::OMIT_VALUE:
case Template::ANY_VALUE:
case Template::ANY_OR_OMIT:
break; // acceptable (?)
case Template::TEMPLATE_INVOKE: // calling a function is not acceptable
body->error("default value can not be a function invocation");
break;
case Template::VALUE_RANGE: {
ValueRange *range = body->get_value_range();
Value *low = range->get_min_v();
if (low != NULL) {
low->get_expr_returntype(exp_val);
}
Value *high = range->get_max_v();
if (high != NULL) {
high->get_expr_returntype(exp_val);
}
break; }
case Template::BSTR_PATTERN:
case Template::HSTR_PATTERN:
case Template::OSTR_PATTERN:
case Template::CSTR_PATTERN:
case Template::USTR_PATTERN:
break; // should be acceptable in all cases (if only fixed strings possible)
case Template::SPECIFIC_VALUE: {
Common::Value *v = body->get_specific_value();
if (v->get_valuetype() == Value::V_REFD) chk_defpar_value(v);
break; }
case Template::ALL_FROM:
FATAL_ERROR("should have been flattened");
break;
case Template::SUPERSET_MATCH:
case Template::SUBSET_MATCH:
case Template::PERMUTATION_MATCH:
case Template::TEMPLATE_LIST:
case Template::COMPLEMENTED_LIST:
case Template::VALUE_LIST:
case Template::CONJUNCTION_MATCH: {
// in template charstring par := charstring : ("foo", "bar", "baz")
size_t num = body->get_nof_comps();
for (size_t i = 0; i < num; ++i) {
const Template *tpl = body->get_temp_byIndex(i);
chk_defpar_template(tpl, exp_val);
}
break; }
case Template::NAMED_TEMPLATE_LIST: {
size_t num = body->get_nof_comps();
for (size_t i = 0; i < num; ++i) {
const NamedTemplate *nt = body->get_namedtemp_byIndex(i);
const Template *tpl = nt->get_template();
chk_defpar_template(tpl, exp_val);
}
break; }
case Template::INDEXED_TEMPLATE_LIST: {
size_t num = body->get_nof_comps();
for (size_t i = 0; i < num; ++i) {
const IndexedTemplate *it = body->get_indexedtemp_byIndex(i);
const Template *tpl = it->get_template();
chk_defpar_template(tpl, exp_val);
}
break; }
case Template::TEMPLATE_REFD: {
Reference *ref = body->get_reference();
Ttcn::ActualParList *aplist = ref->get_parlist();
if (!aplist) break;
size_t num = aplist->get_nof_pars();
for (size_t i = 0; i < num; ++i) {
const Ttcn::ActualPar *ap = aplist->get_par(i);
deeper:
switch (ap->get_selection()) {
case ActualPar::AP_ERROR: {
break; }
case ActualPar::AP_VALUE: {
Value *v = ap->get_Value(); // "v_variable" as the parameter of the template
v->chk();
switch (v->get_valuetype()) {
case Value::V_REFD: {
chk_defpar_value(v);
break; }
default:
break;
}
break; }
case ActualPar::AP_TEMPLATE: {
// A component cannot contain a template definition, parameterized or not.
// Therefore the template this actual par references, cannot be
// a field of a component => no error possible, nothing to do.
break; }
case ActualPar::AP_REF: {
// A template cannot have an out/inout parameter
FATAL_ERROR("Template with out parameter?");
break; }
case ActualPar::AP_DEFAULT: {
ap = ap->get_ActualPar();
goto deeper;
break; }
// no default
} // switch actual par selection
} // next
break; }
case Template::DECODE_MATCH:
chk_defpar_template(body->get_decode_target()->get_Template(), exp_val);
break;
case Template::TEMPLATE_CONCAT:
if (!use_runtime_2) {
FATAL_ERROR("template concatenation in default parameter");
}
chk_defpar_template(body->get_concat_operand(true), exp_val);
chk_defpar_template(body->get_concat_operand(false), exp_val);
break;
case Template::IMPLICATION_MATCH:
chk_defpar_template(body->get_precondition()->get_Template(), exp_val);
chk_defpar_template(body->get_implied_template()->get_Template(), exp_val);
break;
case Template::DYNAMIC_MATCH:
break;
} // switch templatetype
}
// This function is called in two situations:
// 1. FormalParList::chk calls FormalPar::chk to compute the default value
// (make an ActualPar from a TemplateInstance).
// In this case, defval.ti==0, and actual_par contains its old value.
// This case is called only if the formal par has a default value.
// 2. FormalParList::chk_actual_parlist calls FormalPar::chk_actual_par
// to check the parameters supplied by the execute statement to the tc.
// In this case, defval.ap has the value computed in case 1.
ActualPar *FormalPar::chk_actual_par_template(TemplateInstance *actual_par,
Type::expected_value_t exp_val)
{
actual_par->chk(type);
// actual_par->template_body may change: SPECIFIC_VALUE to TEMPLATE_REFD
Definition *fplist_def = my_parlist->get_my_def();
// The parameter list belongs to this definition. If it's a function
// or testcase, it may have a "runs on" clause.
Def_Function *parent_fn = dynamic_cast<Def_Function *>(fplist_def);
Type *runs_on_type = 0;
if (parent_fn) runs_on_type = parent_fn->get_RunsOnType();
else { // not a function; maybe a testcase
Def_Testcase *parent_tc = dynamic_cast<Def_Testcase *>(fplist_def);
if (parent_tc) runs_on_type = parent_tc->get_RunsOnType();
}
if (runs_on_type) {
// If it _has_ a runs on clause, the type must be a component.
if (runs_on_type->get_typetype() != Type::T_COMPONENT) FATAL_ERROR("not component?");
// The default value "shall not refer to elements of the component type
// in the runs on clause"
ComponentTypeBody *runs_on_component = runs_on_type->get_CompBody();
size_t compass = runs_on_component->get_nof_asss();
for (size_t c = 0; c < compass; c++) {
Assignment *ass = runs_on_component->get_ass_byIndex(c);
(void)ass;
}
}
if (exp_val == Type::EXPECTED_STATIC_VALUE
||exp_val == Type::EXPECTED_CONSTANT) {
Ttcn::Template * body = actual_par->get_Template();
chk_defpar_template(body, exp_val);
}
// Rip out the type, derived ref and template from actual_par
// (which may come from a function invocation or the definition
// of the default value) and give it to the new ActualPar.
ActualPar *ret_val = new ActualPar(
new TemplateInstance(actual_par->get_Type(),
actual_par->get_DerivedRef(), actual_par->get_Template()));
// Zero out these members because the caller will soon call delete
// on actual_par, but they now belong to ret_val.
// FIXME: should this really be in here, or outside in the caller before the delete ?
actual_par->release();
if (template_restriction!=TR_NONE) {
bool needs_runtime_check =
ret_val->get_TemplateInstance()->chk_restriction(
"template formal parameter", template_restriction,
ret_val->get_TemplateInstance());
if (needs_runtime_check)
ret_val->set_gen_restriction_check(template_restriction);
}
return ret_val;
}
ActualPar *FormalPar::chk_actual_par_by_ref(TemplateInstance *actual_par,
bool is_template, Type::expected_value_t exp_val)
{
Type *ap_type = actual_par->get_Type();
if (ap_type) {
ap_type->warning("Explicit type specification is useless for an %s",
get_assname());
actual_par->chk_Type(type);
}
Reference *derived_ref = actual_par->get_DerivedRef();
if (derived_ref) {
derived_ref->error("An in-line modified template cannot be used as %s",
get_assname());
actual_par->chk_DerivedRef(type);
}
// needed for the error messages
const char *expected_string = is_template ?
"template variable or template parameter" :
"variable or value parameter";
Template *ap_template = actual_par->get_Template();
if (ap_template->is_Ref()) {
Reference *ref = ap_template->get_Ref();
Common::Assignment *ass = ref->get_refd_assignment_last();
if (!ass) {
delete ref;
return new ActualPar();
}
bool asstype_correct = false;
switch (ass->get_asstype()) {
case A_PAR_VAL_IN:
ass->use_as_lvalue(*ref);
if (get_asstype() == A_PAR_VAL_OUT || get_asstype() == A_PAR_TEMPL_OUT) {
ass->warning("Passing an `in' parameter as another function's `out' parameter");
}
// no break
case A_VAR:
case A_EXCEPTION:
case A_PAR_VAL_OUT:
case A_PAR_VAL_INOUT:
if (!is_template) asstype_correct = true;
break;
case A_PAR_TEMPL_IN:
ass->use_as_lvalue(*ref);
if (get_asstype() == A_PAR_VAL_OUT || get_asstype() == A_PAR_TEMPL_OUT) {
ass->warning("Passing an `in' parameter as another function's `out' parameter");
}
// no break
case A_VAR_TEMPLATE:
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
if (is_template) asstype_correct = true;
break;
default:
break;
}
if (asstype_correct) {
FieldOrArrayRefs *t_subrefs = ref->get_subrefs();
Type *ref_type = ass->get_Type()->get_field_type(t_subrefs, exp_val);
if (ref_type) {
TypeCompatInfo info(my_scope->get_scope_mod(), type, ref_type, true,
false, is_template);
TypeChain l_chain_base;
TypeChain r_chain_base;
if (!type->is_compatible(ref_type, &info, actual_par,
&l_chain_base, &r_chain_base)) {
if (info.is_subtype_error()) {
ref->error("%s", info.get_subtype_error().c_str());
}
else if (!info.is_erroneous()) {
ref->error("Type mismatch: Reference to a %s of type "
"`%s' was expected instead of `%s'", expected_string,
type->get_typename().c_str(), ref_type->get_typename().c_str());
}
else {
// Always use the format string.
ref->error("%s", info.get_error_str_str().c_str());
}
}
else if (type->get_type_refd_last()->get_typetype() != Common::Type::T_COMPONENT &&
(asstype == A_PAR_VAL_OUT || asstype == A_PAR_VAL_INOUT ||
asstype == A_PAR_TEMPL_OUT || asstype == A_PAR_TEMPL_INOUT) &&
!ref_type->is_compatible(type, &info, NULL,
&l_chain_base, &r_chain_base)) {
// run the type compatibility check in the reverse order, too, for
// 'out' and 'inout' parameters (they need to be converted back after
// the function call; except if they're component types)
// this should never fail if the first type compatibility succeeded
FATAL_ERROR("FormalPar::chk_actual_par_by_ref");
}
if (t_subrefs && t_subrefs->refers_to_string_element()) {
ref->error("Reference to a string element of type `%s' cannot be "
"used in this context", ref_type->get_typename().c_str());
}
}
} else {
ref->error("Reference to a %s was expected for an %s instead of %s",
expected_string, get_assname(), ass->get_description().c_str());
}
ActualPar* ret_val_ap = new ActualPar(ref);
// restriction checking if this is a reference to a template variable
// this is an 'out' or 'inout' template parameter
if (is_template && asstype_correct) {
template_restriction_t refd_tr;
switch (ass->get_asstype()) {
case A_VAR_TEMPLATE: {
Def_Var_Template* dvt = dynamic_cast<Def_Var_Template*>(ass);
if (!dvt) FATAL_ERROR("FormalPar::chk_actual_par_by_ref()");
refd_tr = dvt->get_template_restriction();
} break;
case A_PAR_TEMPL_IN:
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT: {
FormalPar* fp = dynamic_cast<FormalPar*>(ass);
if (!fp) FATAL_ERROR("FormalPar::chk_actual_par_by_ref()");
refd_tr = fp->get_template_restriction();
} break;
default:
FATAL_ERROR("FormalPar::chk_actual_par_by_ref()");
break;
}
refd_tr = Template::get_sub_restriction(refd_tr, ref);
if (template_restriction!=refd_tr) {
bool pre_call_check =
Template::is_less_restrictive(template_restriction, refd_tr);
bool post_call_check =
Template::is_less_restrictive(refd_tr, template_restriction);
if (pre_call_check || post_call_check) {
ref->warning("Inadequate restriction on the referenced %s `%s', "
"this may cause a dynamic test case error at runtime",
ass->get_assname(), ref->get_dispname().c_str());
ass->note("Referenced %s is here", ass->get_assname());
}
if (pre_call_check)
ret_val_ap->set_gen_restriction_check(template_restriction);
if (post_call_check)
ret_val_ap->set_gen_post_restriction_check(refd_tr);
}
// for out and inout template parameters of external functions
// always check because we do not trust user written C++ code
if (refd_tr!=TR_NONE) {
switch (my_parlist->get_my_def()->get_asstype()) {
case A_EXT_FUNCTION:
case A_EXT_FUNCTION_RVAL:
case A_EXT_FUNCTION_RTEMP:
ret_val_ap->set_gen_post_restriction_check(refd_tr);
break;
default:
break;
}
}
}
return ret_val_ap;
} else {
actual_par->error("Reference to a %s was expected for an %s",
expected_string, get_assname());
return new ActualPar();
}
}
ActualPar *FormalPar::chk_actual_par_timer(TemplateInstance *actual_par,
Type::expected_value_t exp_val)
{
Type *ap_type = actual_par->get_Type();
if (ap_type) {
ap_type->error("Explicit type specification cannot be used for a "
"timer parameter");
actual_par->chk_Type(0);
}
Reference *derived_ref = actual_par->get_DerivedRef();
if (derived_ref) {
derived_ref->error("An in-line modified template cannot be used as "
"timer parameter");
actual_par->chk_DerivedRef(0);
}
Template *ap_template = actual_par->get_Template();
if (ap_template->is_Ref()) {
Reference *ref = ap_template->get_Ref();
Common::Assignment *ass = ref->get_refd_assignment();
if (!ass) {
delete ref;
return new ActualPar();
}
switch (ass->get_asstype()) {
case A_TIMER: {
ArrayDimensions *dims = ass->get_Dimensions();
if (dims) dims->chk_indices(ref, "timer", false, exp_val);
else if (ref->get_subrefs()) ref->error("Reference to single %s "
"cannot have field or array sub-references",
ass->get_description().c_str());
break; }
case A_PAR_TIMER:
if (ref->get_subrefs()) ref->error("Reference to %s cannot have "
"field or array sub-references", ass->get_description().c_str());
break;
default:
ref->error("Reference to a timer or timer parameter was expected for "
"a timer parameter instead of %s", ass->get_description().c_str());
}
return new ActualPar(ref);
} else {
actual_par->error("Reference to a timer or timer parameter was "
"expected for a timer parameter");
return new ActualPar();
}
}
ActualPar *FormalPar::chk_actual_par_port(TemplateInstance *actual_par,
Type::expected_value_t exp_val)
{
Type *ap_type = actual_par->get_Type();
if (ap_type) {
ap_type->warning("Explicit type specification is useless for a port "
"parameter");
actual_par->chk_Type(type);
}
Reference *derived_ref = actual_par->get_DerivedRef();
if (derived_ref) {
derived_ref->error("An in-line modified template cannot be used as "
"port parameter");
actual_par->chk_DerivedRef(type);
}
Template *ap_template = actual_par->get_Template();
if (ap_template->is_Ref()) {
Reference *ref = ap_template->get_Ref();
Common::Assignment *ass = ref->get_refd_assignment();
if (!ass) {
delete ref;
return new ActualPar();
}
bool asstype_correct = false;
switch (ass->get_asstype()) {
case A_PORT: {
ArrayDimensions *dims = ass->get_Dimensions();
if (dims) dims->chk_indices(ref, "port", false, exp_val);
else if (ref->get_subrefs()) ref->error("Reference to single %s "
"cannot have field or array sub-references",
ass->get_description().c_str());
asstype_correct = true;
break; }
case A_PAR_PORT:
if (ref->get_subrefs()) ref->error("Reference to %s cannot have "
"field or array sub-references", ass->get_description().c_str());
asstype_correct = true;
break;
default:
ref->error("Reference to a port or port parameter was expected for a "
"port parameter instead of %s", ass->get_description().c_str());
}
if (asstype_correct) {
Type *ref_type = ass->get_Type();
if (ref_type && !type->is_identical(ref_type))
ref->error("Type mismatch: Reference to a port or port parameter "
"of type `%s' was expected instead of `%s'",
type->get_typename().c_str(), ref_type->get_typename().c_str());
}
return new ActualPar(ref);
} else {
if (ap_template->get_templatetype() == Template::SPECIFIC_VALUE) {
Value* val = ap_template->get_specific_value();
if (val->get_valuetype() == Common::Value::V_EXPR &&
val->get_optype() == Common::Value::OPTYPE_GET_PORT_REF) {
Value *v = ap_template->get_Value(); // steal the value
v->set_my_governor(type);
type->chk_this_value_ref(v);
type->chk_this_value(v, 0, exp_val, INCOMPLETE_NOT_ALLOWED,
OMIT_NOT_ALLOWED, SUB_CHK);
return new ActualPar(v);
}
}
actual_par->error("Reference to a port or port parameter was expected "
"for a port parameter");
return new ActualPar();
}
}
void FormalPar::use_as_lvalue(const Location& p_loc)
{
switch (asstype) {
case A_PAR_VAL_IN:
case A_PAR_TEMPL_IN:
break;
default:
FATAL_ERROR("FormalPar::use_as_lvalue()");
}
if (!used_as_lvalue) {
Definition *my_def = my_parlist->get_my_def();
if (my_def != NULL && my_def->get_asstype() == A_TEMPLATE)
p_loc.error("Parameter `%s' of the template cannot be passed further "
"as `out' or `inout' parameter", id->get_dispname().c_str());
else {
// update the genname so that all references in the generated code
// will point to the shadow object
if (eval == NORMAL_EVAL && get_defpar_wrapper() == Common::Type::NO_DEFPAR_WRAPPER) {
set_genname(id->get_name() + "_shadow");
}
used_as_lvalue = true;
}
}
}
char* FormalPar::generate_code_defval_template(char* str, TemplateInstance* ti,
const string& name,
template_restriction_t temp_res)
{
Template *temp = ti->get_Template();
Reference *dref = ti->get_DerivedRef();
if (dref != NULL) {
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr, "%s = ", name.c_str());
dref->generate_code(&expr);
str = Code::merge_free_expr(str, &expr);
}
if (use_runtime_2 && TypeConv::needs_conv_refd(temp)) {
str = TypeConv::gen_conv_code_refd(str, name.c_str(), temp);
} else {
// force the re-generation of the template's initialization code
// (this is needed in case it contains non-deterministic function calls)
temp->reset_code_generated();
str = temp->generate_code_init(str, name.c_str());
}
if (temp_res != TR_NONE) {
str = Template::generate_restriction_check_code(str, name.c_str(), temp_res);
}
return str;
}
Common::Type::defpar_wapper_t FormalPar::get_defpar_wrapper() const
{
Definition* def = my_parlist->get_my_def();
if (defval.ap != NULL && def != NULL &&
def->get_my_scope()->is_class_scope()) {
switch (asstype) {
case A_PAR_VAL_IN:
case A_PAR_TEMPL_IN:
return Common::Type::DEFPAR_IN_WRAPPER;
case A_PAR_VAL_OUT:
case A_PAR_VAL_INOUT:
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
case A_PAR_TIMER:
case A_PAR_PORT:
return Common::Type::DEFPAR_OUT_WRAPPER;
default:
FATAL_ERROR("FormalPar::get_defpar_wrapper");
}
}
return Common::Type::NO_DEFPAR_WRAPPER;
}
char* FormalPar::generate_code_defval(char* str)
{
if (!defval.ap || defval_generated) return str;
defval_generated = true;
Common::Type::defpar_wapper_t defpar_wrapper = get_defpar_wrapper();
if (!usage_found && defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER) {
return str;
}
switch (defval.ap->get_selection()) {
case ActualPar::AP_VALUE: {
Value *val = defval.ap->get_Value();
string tmp_id;
if (defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER) {
tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
str = mputprintf(str,
"%s %s;\n",
type->get_genname_value(my_scope).c_str(), tmp_id.c_str());
val->set_gen_class_defpar_prefix();
}
if (use_runtime_2 && TypeConv::needs_conv_refd(val)) {
str = TypeConv::gen_conv_code_refd(str, defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER ?
tmp_id.c_str() : val->get_lhs_name().c_str(), val);
} else {
str = val->generate_code_init(str, defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER ?
tmp_id.c_str() : val->get_lhs_name().c_str());
}
if (defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER) {
str = mputprintf(str, "def_val = %s;\n", tmp_id.c_str());
}
break; }
case ActualPar::AP_TEMPLATE: {
if (!use_runtime_2 || defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER) {
TemplateInstance *ti = defval.ap->get_TemplateInstance();
string tmp_id;
if (defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER) {
tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
str = mputprintf(str,
"%s %s;\n",
type->get_genname_template(my_scope).c_str(), tmp_id.c_str());
ti->set_gen_class_defpar_prefix();
}
str = generate_code_defval_template(str, ti,
defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER ?
tmp_id : ti->get_Template()->get_lhs_name(),
defval.ap->get_gen_restriction_check());
if (defpar_wrapper != Common::Type::NO_DEFPAR_WRAPPER) {
str = mputprintf(str, "def_val = %s;\n", tmp_id.c_str());
}
}
break; }
case ActualPar::AP_REF:
break;
default:
FATAL_ERROR("FormalPar::generate_code()");
}
return str;
}
char* FormalPar::generate_code_defpar_init(char* str)
{
if (defval.ap == NULL || !usage_found) return str;
Common::Type::defpar_wapper_t defpar_wrapper = get_defpar_wrapper();
switch (defval.ap->get_selection()) {
case ActualPar::AP_VALUE:
str = mputprintf(str,
"%s%s%s %s = %s_defpar(this);\n",
(defpar_wrapper == Common::Type::DEFPAR_IN_WRAPPER && !used_as_lvalue) ? "const " : "",
type->get_genname_value(my_scope).c_str(), !used_as_lvalue ? "&" : "",
id->get_name().c_str(), id->get_name().c_str());
// this code also works if the parameter is used as lvalue, no need for shadow objects
used_as_lvalue = false;
break;
case ActualPar::AP_TEMPLATE:
str = mputprintf(str,
"%s%s%s %s = %s_defpar(this);\n",
(defpar_wrapper == Common::Type::DEFPAR_IN_WRAPPER && !used_as_lvalue) ? "const " : "",
type->get_genname_template(my_scope).c_str(), !used_as_lvalue ? "&" : "",
id->get_name().c_str(), id->get_name().c_str());
// this code also works if the parameter is used as lvalue, no need for shadow objects
used_as_lvalue = false;
break;
case ActualPar::AP_REF:
break;
default:
FATAL_ERROR("FormalPar::generate_code_defpar_init()");
}
return str;
}
void FormalPar::generate_code_defval(output_struct *target, bool)
{
if (!defval.ap) return;
Definition* def = my_parlist->get_my_def();
Scope* scope = def != NULL ? def->get_my_scope() : NULL;
bool defpar_wrapper = defval.ap != NULL && scope != NULL && scope->is_class_scope();
switch (defval.ap->get_selection()) {
case ActualPar::AP_VALUE: {
Value *val = defval.ap->get_Value();
const_def cdef;
Code::init_cdef(&cdef);
type->generate_code_object(&cdef, val, false,
defpar_wrapper ? scope->get_scope_class()->get_defpar_type_name(this) : NULL);
Code::merge_cdef(target, &cdef);
Code::free_cdef(&cdef);
break; }
case ActualPar::AP_TEMPLATE: {
if (use_runtime_2 && defpar_wrapper == Common::Type::NO_DEFPAR_WRAPPER) {
break;
}
TemplateInstance *ti = defval.ap->get_TemplateInstance();
Template *temp = ti->get_Template();
const_def cdef;
Code::init_cdef(&cdef);
type->generate_code_object(&cdef, temp, false,
defpar_wrapper ? scope->get_scope_class()->get_defpar_type_name(this) : NULL);
Code::merge_cdef(target, &cdef);
Code::free_cdef(&cdef);
break; }
case ActualPar::AP_REF:
break;
default:
FATAL_ERROR("FormalPar::generate_code()");
}
if (!defpar_wrapper) {
target->functions.post_init = generate_code_defval(target->functions.post_init);
} // otherwise the default value generation is handled by the calling function
}
char *FormalPar::generate_code_fpar(char *str, bool display_unused /* = false */)
{
Definition* def = my_parlist->get_my_def();
Scope* scope = def != NULL ? def->get_my_scope() : NULL;
bool defpar_wrapper = defval.ap != NULL && scope != NULL && scope->is_class_scope();
// the name of the parameter should not be displayed if the parameter is not
// used (to avoid a compiler warning)
bool display_name = (usage_found || display_unused || debugger_active ||
(!enable_set_bound_out_param && (asstype == A_PAR_VAL_OUT || asstype == A_PAR_TEMPL_OUT)));
const char *name_str = display_name ? id->get_name().c_str() : "";
switch (asstype) {
case A_PAR_VAL_IN:
if (eval != NORMAL_EVAL) {
str = mputprintf(str, "Lazy_Fuzzy_Expr<%s>& %s", type->get_genname_value(my_scope).c_str(), name_str);
} else if (defpar_wrapper) {
str = mputprintf(str, "%s %s%s", scope->get_scope_class()->get_defpar_type_name(this), name_str,
display_name ? "_defpar" : "");
} else {
str = mputprintf(str, "const %s& %s", type->get_genname_value(my_scope).c_str(), name_str);
}
break;
case A_PAR_VAL_OUT:
case A_PAR_VAL_INOUT:
case A_PAR_PORT:
if (defpar_wrapper) {
str = mputprintf(str, "%s %s%s", scope->get_scope_class()->get_defpar_type_name(this), name_str,
display_name ? "_defpar" : "");
}
else {
str = mputprintf(str, "%s& %s", type->get_genname_value(my_scope).c_str(),
name_str);
}
break;
case A_PAR_TEMPL_IN:
if (eval != NORMAL_EVAL) {
str = mputprintf(str, "Lazy_Fuzzy_Expr<%s>& %s", type->get_genname_template(my_scope).c_str(), name_str);
} else if (defpar_wrapper) {
str = mputprintf(str, "%s %s%s", scope->get_scope_class()->get_defpar_type_name(this), name_str,
display_name ? "_defpar" : "");
} else {
str = mputprintf(str, "const %s& %s", type->get_genname_template(my_scope).c_str(), name_str);
}
break;
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
if (defpar_wrapper) {
str = mputprintf(str, "%s %s%s", scope->get_scope_class()->get_defpar_type_name(this), name_str,
display_name ? "_defpar" : "");
} else {
str = mputprintf(str, "%s& %s",
type->get_genname_template(my_scope).c_str(), name_str);
}
break;
case A_PAR_TIMER:
if (defpar_wrapper) {
str = mputprintf(str, "%s %s%s", scope->get_scope_class()->get_defpar_type_name(this), name_str,
display_name ? "_defpar" : "");
} else {
str = mputprintf(str, "TIMER& %s", name_str);
}
break;
default:
FATAL_ERROR("FormalPar::generate_code()");
}
return str;
}
string FormalPar::get_reference_name(Scope* scope) const
{
string ret_val;
if (eval != NORMAL_EVAL) {
ret_val += "((";
switch (asstype) {
case A_PAR_TEMPL_IN:
ret_val += type->get_genname_template(scope);
break;
default:
ret_val += type->get_genname_value(scope);
break;
}
ret_val += "&)";
}
ret_val += get_id().get_name();
if (eval != NORMAL_EVAL) {
ret_val += ")";
}
return ret_val;
}
char *FormalPar::generate_code_object(char *str, const char *p_prefix, char refch, bool gen_init)
{
const char *name_str = id->get_name().c_str();
switch (asstype) {
case A_PAR_VAL_IN:
if (eval != NORMAL_EVAL) {
str = mputprintf(str, "Lazy_Fuzzy_Expr<%s> %s%s",
type->get_genname_value(my_scope).c_str(), p_prefix, name_str);
if (gen_init) {
str = mputprintf(str, "(%s)", eval == LAZY_EVAL ? "FALSE" : "TRUE");
}
str = mputstr(str, ";\n");
} else {
str = mputprintf(str, "%s %s%s;\n", type->get_genname_value(my_scope).c_str(), p_prefix, name_str);
}
break;
case A_PAR_VAL_OUT:
case A_PAR_VAL_INOUT:
case A_PAR_PORT:
str = mputprintf(str, "%s%c %s%s;\n",
type->get_genname_value(my_scope).c_str(), refch, p_prefix, name_str);
break;
case A_PAR_TEMPL_IN:
if (eval != NORMAL_EVAL) {
str = mputprintf(str, "Lazy_Fuzzy_Expr<%s> %s%s",
type->get_genname_template(my_scope).c_str(), p_prefix, name_str);
if (gen_init) {
str = mputprintf(str, "(%s)", eval == LAZY_EVAL ? "FALSE" : "TRUE");
}
str = mputstr(str, ";\n");
} else {
str = mputprintf(str, "%s %s%s;\n", type->get_genname_template(my_scope).c_str(), p_prefix, name_str);
}
break;
case A_PAR_TEMPL_OUT:
case A_PAR_TEMPL_INOUT:
str = mputprintf(str, "%s%c %s%s;\n",
type->get_genname_template(my_scope).c_str(), refch, p_prefix, name_str);
break;
case A_PAR_TIMER:
str = mputprintf(str, "TIMER& %s%s;\n", p_prefix, name_str);
break;
default:
FATAL_ERROR("FormalPar::generate_code_object()");
}
return str;
}
char *FormalPar::generate_shadow_object(char *str) const
{
if (used_as_lvalue && usage_found && eval == NORMAL_EVAL) {
const string& t_genname = get_genname();
const char *genname_str = t_genname.c_str();
const char *name_str = id->get_name().c_str();
switch (asstype) {
case A_PAR_VAL_IN:
str = mputprintf(str, "%s %s(%s);\n",
type->get_genname_value(my_scope).c_str(), genname_str, name_str);
break;
case A_PAR_TEMPL_IN:
str = mputprintf(str, "%s %s(%s);\n",
type->get_genname_template(my_scope).c_str(), genname_str, name_str);
break;
default:
break;
}
}
return str;
}
char *FormalPar::generate_code_set_unbound(char *str) const
{
switch (asstype) {
case A_PAR_TEMPL_OUT:
case A_PAR_VAL_OUT:
str = mputprintf(str, "%s.clean_up();\n", id->get_name().c_str());
break;
default:
break;
}
return str;
}
void FormalPar::dump_internal(unsigned level) const
{
DEBUG(level, "%s: %s", get_assname(), id->get_dispname().c_str());
if (type) type->dump(level + 1);
if (checked) {
if (defval.ap) {
DEBUG(level + 1, "default value:");
defval.ap->dump(level + 2);
}
} else {
if (defval.ti) {
DEBUG(level + 1, "default value:");
defval.ti->dump(level + 2);
}
}
}
// =================================
// ===== FormalParList
// =================================
FormalParList::FormalParList(const FormalParList& p)
: Scope(), Location(), pars_v(), pars_m(), min_nof_pars(0), my_def(0),
checked(false), is_startable(false)
{
for (size_t i = 0; i < p.pars_v.size(); ++i) {
add_fp(p.pars_v[i]->clone());
}
}
FormalParList::~FormalParList()
{
size_t nof_pars = pars_v.size();
for (size_t i = 0; i < nof_pars; i++) delete pars_v[i];
pars_v.clear();
pars_m.clear();
}
FormalParList *FormalParList::clone() const
{
return new FormalParList(*this);
}
void FormalParList::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
for (size_t i = 0; i < pars_v.size(); i++) {
FormalPar *par = pars_v[i];
par->set_fullname(p_fullname + "." + par->get_id().get_dispname());
}
}
void FormalParList::set_my_scope(Scope *p_scope)
{
set_parent_scope(p_scope);
Node::set_my_scope(p_scope);
// the scope of parameters is set to the parent scope instead of this
// because they cannot refer to each other
for (size_t i = 0; i < pars_v.size(); i++) pars_v[i]->set_my_scope(p_scope);
}
void FormalParList::add_fp(FormalPar *p_fp)
{
if (!p_fp) FATAL_ERROR("NULL parameter: Ttcn::FormalParList::add_fp()");
pars_v.add(p_fp);
p_fp->set_my_parlist(this);
checked = false;
}
bool FormalParList::has_notused_defval() const
{
for (size_t i = 0; i < pars_v.size(); i++) {
if (pars_v[i]->has_notused_defval())
return true;
}
return false;
}
bool FormalParList::has_only_default_values() const
{
for (size_t i = 0; i < pars_v.size(); i++) {
if (!pars_v[i]->has_defval()) {
return false;
}
}
return true;
}
bool FormalParList::has_fp_withName(const Identifier& p_name)
{
if (!checked) chk(Definition::A_UNDEF);
return pars_m.has_key(p_name.get_name());
}
FormalPar *FormalParList::get_fp_byName(const Identifier& p_name)
{
if (!checked) chk(Definition::A_UNDEF);
return pars_m[p_name.get_name()];
}
bool FormalParList::get_startability()
{
if(!checked) FATAL_ERROR("FormalParList::get_startability()");
return is_startable;
}
Common::Assignment *FormalParList::get_ass_bySRef(Common::Ref_simple *p_ref)
{
if (!p_ref || !checked) FATAL_ERROR("FormalParList::get_ass_bySRef()");
if (p_ref->get_modid() || p_ref->get_reftype() != Ref_simple::REF_BASIC) {
return parent_scope->get_ass_bySRef(p_ref);
}
else {
const string& name = p_ref->get_id()->get_name();
if (pars_m.has_key(name)) return pars_m[name];
else return parent_scope->get_ass_bySRef(p_ref);
}
}
bool FormalParList::has_ass_withId(const Identifier& p_id)
{
if (!checked) FATAL_ERROR("Ttcn::FormalParList::has_ass_withId()");
return pars_m.has_key(p_id.get_name())
|| parent_scope->has_ass_withId(p_id);
}
void FormalParList::set_genname(const string& p_prefix)
{
for (size_t i = 0; i < pars_v.size(); i++) {
FormalPar *par = pars_v[i];
const string& par_name = par->get_id().get_name();
if (par->get_asstype() != Definition::A_PAR_TIMER)
par->get_Type()->set_genname(p_prefix, par_name);
if (par->has_defval_checked()) {
string embedded_genname(p_prefix);
embedded_genname += '_';
embedded_genname += par_name;
embedded_genname += "_defval";
ActualPar *defval = par->get_defval();
switch (defval->get_selection()) {
case ActualPar::AP_ERROR:
case ActualPar::AP_REF:
break;
case ActualPar::AP_VALUE: {
Value *v = defval->get_Value();
v->set_genname_prefix("const_");
v->set_genname_recursive(embedded_genname);
break; }
case ActualPar::AP_TEMPLATE: {
Template *t = defval->get_TemplateInstance()->get_Template();
t->set_genname_prefix("template_");
t->set_genname_recursive(embedded_genname);
break; }
default:
FATAL_ERROR("FormalParList::set_genname()");
}
}
}
}
void FormalParList::chk(Definition::asstype_t deftype)
{
if (checked) return;
checked = true;
min_nof_pars = 0;
is_startable = true;
Error_Context cntxt(this, "In formal parameter list");
for (size_t i = 0; i < pars_v.size(); i++) {
FormalPar *par = pars_v[i];
const Identifier& id = par->get_id();
const string& name = id.get_name();
const char *dispname = id.get_dispname().c_str();
if (pars_m.has_key(name)) {
par->error("Duplicate parameter with name `%s'", dispname);
pars_m[name]->note("Previous definition of `%s' is here", dispname);
} else {
pars_m.add(name, par);
if (parent_scope && parent_scope->has_ass_withId(id)) {
Reference ref(0, id.clone());
ref.set_my_scope(this);
Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref);
if (!ass) FATAL_ERROR("FormalParList::chk()");
if (parent_scope->get_scope_class() == NULL ||
!ass->get_my_scope()->is_class_scope()) {
par->error("Parameter name `%s' is not unique in the scope "
"hierarchy", dispname);
ass->note("Symbol `%s' is already defined here in a higher scope "
"unit", dispname);
}
}
}
Error_Context cntxt2(par, "In parameter `%s'", dispname);
par->chk();
// check whether the parameter type is allowed
switch (deftype) {
case Definition::A_TEMPLATE:
case Definition::A_CONSTRUCTOR:
switch (par->get_asstype()) {
case Definition::A_PAR_VAL_IN:
case Definition::A_PAR_TEMPL_IN:
// these are allowed
break;
default:
par->error("A %s cannot have %s",
deftype == Definition::A_TEMPLATE ? "template" : "constructor",
par->get_assname());
}
break;
case Definition::A_TESTCASE:
switch (par->get_asstype()) {
case Definition::A_PAR_TIMER:
case Definition::A_PAR_PORT:
// these are forbidden
par->error("A testcase cannot have %s", par->get_assname());
break;
default:
break;
}
break;
case Definition::A_PORT:
switch (par->get_asstype()) {
case Definition::A_PAR_VAL:
case Definition::A_PAR_VAL_IN:
case Definition::A_PAR_VAL_OUT:
case Definition::A_PAR_VAL_INOUT:
// these are allowed
par->get_Type()->chk_map_param(par);
break;
default:
par->error("The `map'/`unmap' parameters of a port type cannot have %s",
par->get_assname());
break;
}
break;
default:
// everything is allowed for functions and altsteps
break;
}
//startability chk
switch(par->get_asstype()) {
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_TEMPL_OUT:
if (is_startable &&
(par->get_Type()->is_component_internal() ||
(par->get_Type()->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS))) {
is_startable = false;
}
break;
default:
is_startable = false;
break;
}
if (!par->has_defval()) min_nof_pars = i + 1;
// the last parameter without a default value determines the minimum
}
}
// check that @lazy and @fuzzy paramterization are not used in cases currently unsupported
void FormalParList::chk_noLazyFuzzyParams() {
Error_Context cntxt(this, "In formal parameter list");
for (size_t i = 0; i < pars_v.size(); i++) {
FormalPar *par = pars_v[i];
if (par->get_eval_type() != NORMAL_EVAL) {
par->error("Formal parameter `%s' cannot be @%s, not supported in this case.",
par->get_id().get_dispname().c_str(),
par->get_eval_type() == LAZY_EVAL ? "lazy" : "fuzzy");
}
}
}
void FormalParList::chk_startability(const char *p_what, const char *p_name,
Location* caller_location)
{
if(!checked) FATAL_ERROR("FormalParList::chk_startability()");
bool has_out_or_inout = false;
for (size_t i = 0; i < pars_v.size(); i++) {
FormalPar *par = pars_v[i];
switch (par->get_asstype()) {
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_TEMPL_OUT:
has_out_or_inout = true;
// no break
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_TEMPL_IN:
if (!is_startable && par->get_Type()->is_component_internal()) {
map<Type*,void> type_chain;
char* err_str = mprintf("a parameter or embedded in a parameter of "
"a function used in a start operation. "
"%s `%s' cannot be started on a parallel test component "
"because of `%s'", p_what, p_name, par->get_description().c_str());
par->get_Type()->chk_component_internal(type_chain, err_str);
Free(err_str);
}
if (!is_startable &&
par->get_Type()->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) {
caller_location->error("%s `%s' cannot be started on a parallel test component "
"because parameter `%s' is of a class type",
p_what, p_name, par->get_id().get_dispname().c_str());
}
break;
default:
if (!is_startable) {
par->error("%s `%s' cannot be started on a parallel test component "
"because it has %s", p_what, p_name, par->get_description().c_str());
}
}
}
if (has_out_or_inout) {
caller_location->warning("The `out' and `inout' parameters of functions "
"started on parallel test components will remain unchanged at the end "
"of the operation.");
}
}
void FormalParList::chk_compatibility(FormalParList* p_fp_list,
const char* where)
{
size_t nof_type_pars = pars_v.size();
size_t nof_function_pars = p_fp_list->pars_v.size();
// check for the number of parameters
if (nof_type_pars != nof_function_pars) {
p_fp_list->error("Too %s parameters: %lu was expected instead of %lu",
nof_type_pars < nof_function_pars ? "many" : "few",
static_cast<unsigned long>( nof_type_pars ), static_cast<unsigned long>( nof_function_pars)) ;
}
size_t upper_limit =
nof_type_pars < nof_function_pars ? nof_type_pars : nof_function_pars;
for (size_t i = 0; i < upper_limit; i++) {
FormalPar *type_par = pars_v[i];
FormalPar *function_par = p_fp_list->pars_v[i];
Error_Context cntxt(function_par, "In parameter #%lu",
static_cast<unsigned long> (i + 1));
FormalPar::asstype_t type_par_asstype = type_par->get_asstype();
FormalPar::asstype_t function_par_asstype = function_par->get_asstype();
// check for parameter kind equivalence
// (in, out or inout / value or template)
if (type_par_asstype != function_par_asstype) {
function_par->error("The kind of the parameter is not the same as in "
"type `%s': %s was expected instead of %s", where,
type_par->get_assname(), function_par->get_assname());
}
// check for type equivalence
if (type_par_asstype != FormalPar::A_PAR_TIMER &&
function_par_asstype != FormalPar::A_PAR_TIMER) {
Type *type_par_type = type_par->get_Type();
Type *function_par_type = function_par->get_Type();
if (!type_par_type->is_identical(function_par_type)) {
function_par_type->error("The type of the parameter is not the same "
"as in type `%s': `%s' was expected instead of `%s'", where,
type_par_type->get_typename().c_str(),
function_par_type->get_typename().c_str());
} else if (type_par_type->get_sub_type() && function_par_type->get_sub_type() &&
(type_par_type->get_sub_type()->get_subtypetype()==function_par_type->get_sub_type()->get_subtypetype()) &&
(!type_par_type->get_sub_type()->is_compatible(function_par_type->get_sub_type()))) {
// TODO: maybe equivalence should be checked, or maybe that is too strict
function_par_type->error(
"Subtype mismatch: subtype %s has no common value with subtype %s",
type_par_type->get_sub_type()->to_string().c_str(),
function_par_type->get_sub_type()->to_string().c_str());
}
}
// check for template restriction equivalence
if (type_par->get_template_restriction()!=
function_par->get_template_restriction()) {
function_par->error("The template restriction of the parameter is "
"not the same as in type `%s': %s restriction was expected instead "
"of %s restriction", where,
type_par->get_template_restriction()==TR_NONE ? "no" :
Template::get_restriction_name(type_par->get_template_restriction()),
function_par->get_template_restriction()==TR_NONE ? "no" :
Template::get_restriction_name(function_par->
get_template_restriction()));
}
// check for @lazy equivalence
if (type_par->get_eval_type()!=function_par->get_eval_type()) {
function_par->error("Parameter evaluation type (normal, @lazy or @fuzzy) mismatch");
}
// check for name equivalence
const Identifier& type_par_id = type_par->get_id();
const Identifier& function_par_id = function_par->get_id();
if (type_par_id != function_par_id) {
function_par->warning("The name of the parameter is not the same "
"as in type `%s': `%s' was expected instead of `%s'", where,
type_par_id.get_dispname().c_str(),
function_par_id.get_dispname().c_str());
}
}
}
bool FormalParList::fold_named_and_chk(ParsedActualParameters *p_paps,
ActualParList *p_aplist)
{
const size_t num_named = p_paps->get_nof_nps();
const size_t num_unnamed = p_paps->get_nof_tis();
size_t num_actual = num_unnamed;
// Construct a map to tell us what index a FormalPar has
typedef map<FormalPar*, size_t> formalpar_map_t;
formalpar_map_t formalpar_map;
size_t num_fp = get_nof_fps();
for (size_t fpx = 0; fpx < num_fp; ++fpx) {
FormalPar *fp = get_fp_byIndex(fpx);
formalpar_map.add(fp, new size_t(fpx));
}
// Go through the named parameters
for (size_t i = 0; i < num_named; ++i) {
NamedParam *np = p_paps->extract_np_byIndex(i);
// We are now responsible for np.
if (has_fp_withName(*np->get_name())) {
// there is a formal parameter with that name
FormalPar *fp = get_fp_byName(*np->get_name());
const size_t is_at = *formalpar_map[fp]; // the index of the formal par
if (is_at >= num_actual) {
// There is no actual par in the unnamed part.
// Create one from the named param.
// First, pad the gap with '-'
for (; num_actual < is_at; ++num_actual) {
Template *not_used;
if (pars_v[num_actual]->has_defval()) {
not_used = new Template(Template::TEMPLATE_NOTUSED);
}
else { // cannot use '-' if no default value
not_used = new Template(Template::TEMPLATE_ERROR);
}
TemplateInstance *new_ti = new TemplateInstance(0, 0, not_used);
// Conjure a location info at the beginning of the unnamed part
// (that is, the beginning of the actual parameter list)
new_ti->set_location(p_paps->get_tis()->get_filename(),
p_paps->get_tis()->get_first_line(),
p_paps->get_tis()->get_first_column(), 0, 0);
p_paps->get_tis()->add_ti(new_ti);
}
TemplateInstance * namedti = np->extract_ti();
p_paps->get_tis()->add_ti(namedti);
++num_actual;
} else {
// There is already an actual par at that position, fetch it
TemplateInstance * ti = p_paps->get_tis()->get_ti_byIndex(is_at);
Template::templatetype_t tt = ti->get_Template()->get_templatetype();
if (is_at >= num_unnamed && !ti->get_Type() && !ti->get_DerivedRef()
&& (tt == Template::TEMPLATE_NOTUSED || tt == Template::TEMPLATE_ERROR)) {
// NotUsed in the named part => padding
np->error("Named parameter `%s' out of order",
np->get_name()->get_dispname().c_str());
} else {
// attempt to override an original unnamed param with a named one
np->error("Formal parameter `%s' assigned more than once",
np->get_name()->get_dispname().c_str());
}
}
}
else { // no formal parameter with that name
char * nam = 0;
switch (my_def->get_asstype()) {
case Common::Assignment::A_TYPE: {
Type *t = my_def->get_Type();
switch (t ? t->get_typetype() : 0) {
case Type::T_FUNCTION:
nam = mcopystr("Function reference");
break;
case Type::T_ALTSTEP:
nam = mcopystr("Altstep reference");
break;
case Type::T_TESTCASE:
nam = mcopystr("Testcase reference");
break;
default:
FATAL_ERROR("FormalParList::chk_actual_parlist() "
"Unexpected type %s", t->get_typename().c_str());
} // switch(typetype)
break; }
default:
nam = mcopystr(my_def->get_assname());
break;
} // switch(asstype)
*nam &= ~('a'-'A'); // Make the first letter uppercase
p_paps->get_tis()->error("%s `%s' has no formal parameter `%s'",
nam,
my_def->get_fullname().c_str(),
np->get_name()->get_dispname().c_str());
Free(nam);
}
delete np;
}
// Cleanup
for (size_t fpx = 0; fpx < num_fp; ++fpx) {
delete formalpar_map.get_nth_elem(fpx);
}
formalpar_map.clear();
return chk_actual_parlist(p_paps->get_tis(), p_aplist);
}
bool FormalParList::chk_actual_parlist(TemplateInstances *p_tis,
ActualParList *p_aplist)
{
size_t formal_pars = pars_v.size();
size_t actual_pars = p_tis->get_nof_tis();
// p_aplist->get_nof_pars() is usually 0 on entry
bool error_flag = false;
if (min_nof_pars == formal_pars) {
// none of the parameters have default value
if (actual_pars != formal_pars) {
p_tis->error("Too %s parameters: %lu was expected "
"instead of %lu", actual_pars < formal_pars ? "few" : "many",
static_cast<unsigned long>( formal_pars), static_cast<unsigned long>( actual_pars));
error_flag = true;
}
} else {
// some parameters have default value
if (actual_pars < min_nof_pars) {
p_tis->error("Too few parameters: at least %lu "
"was expected instead of %lu",
static_cast<unsigned long>( min_nof_pars), static_cast<unsigned long>( actual_pars));
error_flag = true;
} else if (actual_pars > formal_pars) {
p_tis->error("Too many parameters: at most %lu "
"was expected instead of %lu",
static_cast<unsigned long>( formal_pars), static_cast<unsigned long>( actual_pars));
error_flag = true;
}
}
// Do not check actual parameters in excess of the formal ones
size_t upper_limit = actual_pars < formal_pars ? actual_pars : formal_pars;
for (size_t i = 0; i < upper_limit; i++) {
TemplateInstance *ti = p_tis->get_ti_byIndex(i);
// the formal parameter for the current actual parameter
FormalPar *fp = pars_v[i];
Error_Context cntxt(ti, "In parameter #%lu for `%s'",
static_cast<unsigned long> (i + 1), fp->get_id().get_dispname().c_str());
if (!ti->get_Type() && !ti->get_DerivedRef() && ti->get_Template()
->get_templatetype() == Template::TEMPLATE_NOTUSED) {
if (fp->has_defval_checked()) {
ActualPar *defval = fp->get_defval();
p_aplist->add(new ActualPar(defval));
if (defval->is_erroneous()) error_flag = true;
} else {
ti->error("Not used symbol (`-') cannot be used for parameter "
"that does not have default value");
p_aplist->add(new ActualPar());
error_flag = true;
}
} else if (!ti->get_Type() && !ti->get_DerivedRef() && ti->get_Template()
->get_templatetype() == Template::TEMPLATE_ERROR) {
ti->error("Parameter not specified");
} else {
ActualPar *ap = fp->chk_actual_par(ti, Type::EXPECTED_DYNAMIC_VALUE);
p_aplist->add(ap);
if (ap->is_erroneous()) error_flag = true;
}
}
// The rest of formal parameters have no corresponding actual parameters.
// Create actual parameters for them based on their default values
// (which must exist).
for (size_t i = upper_limit; i < formal_pars; i++) {
FormalPar *fp = pars_v[i];
if (fp->has_defval_checked()) {
ActualPar *defval = fp->get_defval();
p_aplist->add(new ActualPar(defval));
if (defval->is_erroneous()) error_flag = true;
} else {
p_aplist->add(new ActualPar()); // erroneous
error_flag = true;
}
}
return error_flag;
}
bool FormalParList::chk_activate_argument(ActualParList *p_aplist,
const char* p_description)
{
bool ret_val = true;
for(size_t i = 0; i < p_aplist->get_nof_pars(); i++) {
ActualPar *t_ap = p_aplist->get_par(i);
FormalPar *t_fp = pars_v[i];
if (t_fp->get_eval_type() != NORMAL_EVAL) {
t_ap->get_location()->error("Activating a default altstep with @lazy or "
"@fuzzy parameters is not supported");
}
if(t_ap->get_selection() != ActualPar::AP_REF) continue;
switch(t_fp->get_asstype()) {
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_TIMER:
//the checking shall be performed for these parameter types
break;
case Common::Assignment::A_PAR_PORT:
// port parameters are always correct because ports can be defined
// only in component types
continue;
default:
FATAL_ERROR("FormalParList::chk_activate_argument()");
}
Reference *t_ref = t_ap->get_Ref();
Common::Assignment *t_par_ass = t_ref->get_refd_assignment();
if(!t_par_ass) FATAL_ERROR("FormalParList::chk_activate_argument()");
switch (t_par_ass->get_asstype()) {
case Common::Assignment::A_VAR:
case Common::Assignment::A_EXCEPTION: // TODO: can exceptions be of 'default' type?
case Common::Assignment::A_VAR_TEMPLATE:
case Common::Assignment::A_TIMER:
// it is not allowed to pass references of local variables or timers
if (t_par_ass->is_local()) {
t_ref->error("Parameter #%lu of %s refers to %s, which is a local "
"definition within a statement block and may have shorter "
"lifespan than the activated default. Only references to "
"variables and timers defined in the component type can be passed "
"to activated defaults", static_cast<unsigned long> (i + 1), p_description,
t_par_ass->get_description().c_str());
ret_val = false;
}
break;
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
case Common::Assignment::A_PAR_TIMER: {
// it is not allowed to pass references pointing to formal parameters
// except for activate() statements within testcases
// note: all defaults are deactivated at the end of the testcase
FormalPar *t_refd_fp = dynamic_cast<FormalPar*>(t_par_ass);
if (!t_refd_fp) FATAL_ERROR("FormalParList::chk_activate_argument()");
FormalParList *t_fpl = t_refd_fp->get_my_parlist();
if (!t_fpl || !t_fpl->my_def)
FATAL_ERROR("FormalParList::chk_activate_argument()");
if (t_fpl->my_def->get_asstype() != Common::Assignment::A_TESTCASE) {
t_ref->error("Parameter #%lu of %s refers to %s, which may have "
"shorter lifespan than the activated default. Only references to "
"variables and timers defined in the component type can be passed "
"to activated defaults", static_cast<unsigned long> (i + 1), p_description,
t_par_ass->get_description().c_str());
ret_val = false;
} }
default:
break;
}
}
return ret_val;
}
char *FormalParList::generate_code(char *str, size_t display_unused /* = 0 */)
{
for (size_t i = 0; i < pars_v.size(); i++) {
if (i > 0) str = mputstr(str, ", ");
str = pars_v[i]->generate_code_fpar(str, i < display_unused);
}
return str;
}
char* FormalParList::generate_code_defval(char* str)
{
for (size_t i = 0; i < pars_v.size(); i++) {
str = pars_v[i]->generate_code_defval(str);
}
return str;
}
char* FormalParList::generate_code_defpar_init(char* str)
{
for (size_t i = 0; i < pars_v.size(); i++) {
str = pars_v[i]->generate_code_defpar_init(str);
}
return str;
}
void FormalParList::generate_code_defval(output_struct *target)
{
for (size_t i = 0; i < pars_v.size(); i++) {
pars_v[i]->generate_code_defval(target);
}
}
char *FormalParList::generate_code_actual_parlist(char *str,
const char *p_prefix)
{
for (size_t i = 0; i < pars_v.size(); i++) {
if (i > 0) str = mputstr(str, ", ");
str = mputstr(str, p_prefix);
str = mputstr(str, pars_v[i]->get_id().get_name().c_str());
}
return str;
}
char *FormalParList::generate_code_object(char *str, const char *p_prefix, char refch, bool gen_init)
{
for (size_t i = 0; i < pars_v.size(); i++)
str = pars_v[i]->generate_code_object(str, p_prefix, refch, gen_init);
return str;
}
char *FormalParList::generate_shadow_objects(char *str) const
{
for (size_t i = 0; i < pars_v.size(); i++)
str = pars_v[i]->generate_shadow_object(str);
return str;
}
char *FormalParList::generate_code_set_unbound(char *str) const
{
if (enable_set_bound_out_param) return str;
for (size_t i = 0; i < pars_v.size(); i++)
str = pars_v[i]->generate_code_set_unbound(str);
return str;
}
void FormalParList::dump(unsigned level) const
{
size_t nof_pars = pars_v.size();
DEBUG(level, "formal parameters: %lu pcs.", static_cast<unsigned long>( nof_pars));
for(size_t i = 0; i < nof_pars; i++) pars_v[i]->dump(level + 1);
}
// =================================
// ===== ActualPar
// =================================
ActualPar::ActualPar(Value *v)
: Node(), selection(AP_VALUE), my_scope(0), gen_restriction_check(TR_NONE),
gen_post_restriction_check(TR_NONE)
{
if (!v) FATAL_ERROR("ActualPar::ActualPar()");
val = v;
}
ActualPar::ActualPar(TemplateInstance *t)
: Node(), selection(AP_TEMPLATE), my_scope(0),
gen_restriction_check(TR_NONE), gen_post_restriction_check(TR_NONE)
{
if (!t) FATAL_ERROR("ActualPar::ActualPar()");
temp = t;
}
ActualPar::ActualPar(Reference *r)
: Node(), selection(AP_REF), my_scope(0), gen_restriction_check(TR_NONE),
gen_post_restriction_check(TR_NONE)
{
if (!r) FATAL_ERROR("ActualPar::ActualPar()");
ref = r;
}
ActualPar::ActualPar(ActualPar *a)
: Node(), selection(AP_DEFAULT), my_scope(0),
gen_restriction_check(TR_NONE), gen_post_restriction_check(TR_NONE)
{
if (!a) FATAL_ERROR("ActualPar::ActualPar()");
act = a;
}
ActualPar::ActualPar(const ActualPar& p)
: Node(), selection(p.selection), my_scope(p.my_scope),
gen_restriction_check(p.gen_restriction_check),
gen_post_restriction_check(p.gen_post_restriction_check)
{
switch(selection) {
case AP_ERROR:
break;
case AP_VALUE:
val = p.val->clone();
break;
case AP_TEMPLATE:
temp = p.temp->clone();
break;
case AP_REF:
ref = p.ref->clone();
break;
case AP_DEFAULT:
act = p.act;
break;
default:
FATAL_ERROR("ActualPar::ActualPar()");
}
}
ActualPar::~ActualPar()
{
switch(selection) {
case AP_ERROR:
break;
case AP_VALUE:
delete val;
break;
case AP_TEMPLATE:
delete temp;
break;
case AP_REF:
delete ref;
break;
case AP_DEFAULT:
break; // nothing to do with act
default:
FATAL_ERROR("ActualPar::~ActualPar()");
}
}
ActualPar *ActualPar::clone() const
{
return new ActualPar(*this);
}
void ActualPar::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
switch(selection) {
case AP_ERROR:
break;
case AP_VALUE:
val->set_fullname(p_fullname);
break;
case AP_TEMPLATE:
temp->set_fullname(p_fullname);
break;
case AP_REF:
ref->set_fullname(p_fullname);
break;
case AP_DEFAULT:
break;
default:
FATAL_ERROR("ActualPar::set_fullname()");
}
}
void ActualPar::set_my_scope(Scope *p_scope)
{
my_scope = p_scope;
switch(selection) {
case AP_ERROR:
break;
case AP_VALUE:
val->set_my_scope(p_scope);
break;
case AP_TEMPLATE:
temp->set_my_scope(p_scope);
break;
case AP_REF:
ref->set_my_scope(p_scope);
break;
case AP_DEFAULT:
switch (act->selection) {
case AP_REF:
ref->set_my_scope(p_scope);
break;
case AP_VALUE:
break;
case AP_TEMPLATE:
break;
default:
FATAL_ERROR("ActualPar::set_my_scope()");
}
break;
default:
FATAL_ERROR("ActualPar::set_my_scope()");
}
}
Value *ActualPar::get_Value() const
{
if (selection != AP_VALUE) FATAL_ERROR("ActualPar::get_Value()");
return val;
}
TemplateInstance *ActualPar::get_TemplateInstance() const
{
if (selection != AP_TEMPLATE)
FATAL_ERROR("ActualPar::get_TemplateInstance()");
return temp;
}
Reference *ActualPar::get_Ref() const
{
if (selection != AP_REF) FATAL_ERROR("ActualPar::get_Ref()");
return ref;
}
ActualPar *ActualPar::get_ActualPar() const
{
if (selection != AP_DEFAULT) FATAL_ERROR("ActualPar::get_ActualPar()");
return act;
}
void ActualPar::chk_recursions(ReferenceChain& refch)
{
switch (selection) {
case AP_VALUE:
refch.mark_state();
val->chk_recursions(refch);
refch.prev_state();
break;
case AP_TEMPLATE: {
Reference *derived_ref = temp->get_DerivedRef();
if (derived_ref) {
ActualParList *parlist = derived_ref->get_parlist();
if (parlist) {
refch.mark_state();
parlist->chk_recursions(refch);
refch.prev_state();
}
}
Ttcn::Def_Template* defTemp = temp->get_Referenced_Base_Template();
if (defTemp) {
refch.mark_state();
refch.add(defTemp->get_fullname());
refch.prev_state();
}
refch.mark_state();
temp->get_Template()->chk_recursions(refch);
refch.prev_state();
}
default:
break;
}
}
void ActualPar::chk_ctor_defpar(bool default_ctor, bool in_base_call)
{
switch (selection) {
case AP_VALUE:
val->chk_ctor_defpar(default_ctor, in_base_call);
break;
case AP_TEMPLATE:
temp->chk_ctor_defpar(default_ctor, in_base_call);
break;
case AP_REF:
ref->chk_ctor_defpar(default_ctor, in_base_call);
break;
default:
break;
}
}
void ActualPar::chk_class_member(ClassTypeBody* p_class)
{
switch (selection) {
case AP_VALUE:
val->chk_class_member(p_class);
break;
case AP_TEMPLATE:
temp->chk_class_member(p_class);
break;
case AP_REF:
ref->chk_class_member(p_class);
break;
default:
break;
}
}
bool ActualPar::has_single_expr(FormalPar* formal_par)
{
switch (selection) {
case AP_VALUE:
return val->has_single_expr();
case AP_TEMPLATE:
if (gen_restriction_check!=TR_NONE ||
gen_post_restriction_check!=TR_NONE) return false;
return temp->has_single_expr();
case AP_REF:
if (gen_restriction_check!=TR_NONE ||
gen_post_restriction_check!=TR_NONE) return false;
if (use_runtime_2 && ref->get_subrefs() != NULL) {
FieldOrArrayRefs* subrefs = ref->get_subrefs();
for (size_t i = 0; i < subrefs->get_nof_refs(); ++i) {
if (FieldOrArrayRef::ARRAY_REF == subrefs->get_ref(i)->get_type()) {
return false;
}
}
}
// check whether type conversion is needed
if (use_runtime_2 && formal_par != NULL &&
formal_par->get_asstype() != Common::Assignment::A_PAR_TIMER &&
formal_par->get_asstype() != Common::Assignment::A_PAR_PORT) {
bool is_template_par = false;
if (formal_par->get_asstype() == Common::Assignment::A_PAR_TEMPL_INOUT ||
formal_par->get_asstype() == Common::Assignment::A_PAR_TEMPL_OUT) {
is_template_par = true;
}
Common::Assignment* ass = ref->get_refd_assignment();
Type* actual_par_type = ass->get_Type()->get_field_type(ref->get_subrefs(),
is_template_par ? Type::EXPECTED_TEMPLATE : Type::EXPECTED_DYNAMIC_VALUE)->
get_type_refd_last();
Type* formal_par_type = formal_par->get_Type()->get_type_refd_last();
if (my_scope->get_scope_mod()->needs_type_conv(
actual_par_type, formal_par_type)) {
return false;
}
}
return ref->has_single_expr();
case AP_DEFAULT:
return !use_runtime_2 || act->selection != AP_TEMPLATE;
default:
FATAL_ERROR("ActualPar::has_single_expr()");
return false;
}
}
void ActualPar::set_code_section(
GovernedSimple::code_section_t p_code_section)
{
switch (selection) {
case AP_VALUE:
val->set_code_section(p_code_section);
break;
case AP_TEMPLATE:
temp->set_code_section(p_code_section);
break;
case AP_REF:
ref->set_code_section(p_code_section);
default:
break;
}
}
void ActualPar::set_gen_class_defpar_prefix()
{
switch (selection) {
case AP_VALUE:
val->set_gen_class_defpar_prefix();
break;
case AP_TEMPLATE:
temp->set_gen_class_defpar_prefix();
break;
case AP_REF:
ref->set_gen_class_defpar_prefix();
break;
default:
break;
}
}
void ActualPar::set_gen_class_base_call_postfix()
{
switch (selection) {
case AP_VALUE:
val->set_gen_class_base_call_postfix();
break;
case AP_TEMPLATE:
temp->set_gen_class_base_call_postfix();
break;
case AP_REF:
ref->set_gen_class_base_call_postfix();
break;
default:
break;
}
}
void ActualPar::generate_code(expression_struct *expr, bool copy_needed,
FormalPar* formal_par) const
{
param_eval_t param_eval = formal_par != NULL ? formal_par->get_eval_type() : NORMAL_EVAL;
bool used_as_lvalue = formal_par != NULL ? formal_par->get_used_as_lvalue() : false;
switch (selection) {
case AP_VALUE:
if (param_eval != NORMAL_EVAL) { // copy_needed doesn't matter in this case
LazyFuzzyParamData::init(used_as_lvalue);
LazyFuzzyParamData::generate_code(expr, val, my_scope, param_eval == LAZY_EVAL);
LazyFuzzyParamData::clean();
} else {
char* expr_expr = NULL;
if (use_runtime_2 && TypeConv::needs_conv_refd(val)) {
// Generate everything to preamble to be able to tackle the wrapper
// constructor call. TODO: Reduce the number of temporaries created.
const string& tmp_id = val->get_temporary_id();
const char *tmp_id_str = tmp_id.c_str();
expr->preamble = mputprintf(expr->preamble, "%s %s;\n",
val->get_my_governor()->get_genname_value(my_scope).c_str(),
tmp_id_str);
expr->preamble = TypeConv::gen_conv_code_refd(expr->preamble,
tmp_id_str, val);
expr_expr = mputstr(expr_expr, tmp_id_str);
} else {
expression_struct val_expr;
Code::init_expr(&val_expr);
val->generate_code_expr(&val_expr);
if (val_expr.preamble != NULL) {
expr->preamble = mputstr(expr->preamble, val_expr.preamble);
}
if (val_expr.postamble == NULL) {
if (formal_par != NULL &&
formal_par->get_defpar_wrapper() != Common::Type::NO_DEFPAR_WRAPPER) {
expr_expr = mputprintf(expr_expr, "%s(%s)",
val->get_my_governor()->get_genname_value(my_scope).c_str(), val_expr.expr);
}
else {
expr_expr = mputstr(expr_expr, val_expr.expr);
}
}
else {
// make sure the postambles of the parameters are executed before the
// function call itself (needed if the value contains function calls
// with lazy or fuzzy parameters)
const string& tmp_id = val->get_temporary_id();
expr->preamble = mputprintf(expr->preamble, "%s %s(%s);\n",
val->get_my_governor()->get_genname_value(my_scope).c_str(),
tmp_id.c_str(), val_expr.expr);
expr->preamble = mputstr(expr->preamble, val_expr.postamble);
expr_expr = mputstr(expr_expr, tmp_id.c_str());
copy_needed = false; // already copied
}
Code::free_expr(&val_expr);
}
if (copy_needed) expr->expr = mputprintf(expr->expr, "%s(",
val->get_my_governor()->get_genname_value(my_scope).c_str());
expr->expr = mputstr(expr->expr, expr_expr);
Free(expr_expr);
if (copy_needed) expr->expr = mputc(expr->expr, ')');
}
break;
case AP_TEMPLATE:
if (param_eval != NORMAL_EVAL) { // copy_needed doesn't matter in this case
LazyFuzzyParamData::init(used_as_lvalue);
LazyFuzzyParamData::generate_code(expr, temp, gen_restriction_check, my_scope,
param_eval == LAZY_EVAL);
LazyFuzzyParamData::clean();
} else {
char* expr_expr = NULL;
if (use_runtime_2 && TypeConv::needs_conv_refd(temp->get_Template())) {
const string& tmp_id = temp->get_Template()->get_temporary_id();
const char *tmp_id_str = tmp_id.c_str();
expr->preamble = mputprintf(expr->preamble, "%s %s;\n",
temp->get_Template()->get_my_governor()
->get_genname_template(my_scope).c_str(), tmp_id_str);
expr->preamble = TypeConv::gen_conv_code_refd(expr->preamble,
tmp_id_str, temp->get_Template());
// Not incorporated into gen_conv_code() yet.
if (gen_restriction_check != TR_NONE)
expr->preamble = Template::generate_restriction_check_code(
expr->preamble, tmp_id_str, gen_restriction_check);
expr_expr = mputstr(expr_expr, tmp_id_str);
} else {
expression_struct temp_expr;
Code::init_expr(&temp_expr);
temp->generate_code(&temp_expr, gen_restriction_check);
if (temp_expr.preamble != NULL) {
expr->preamble = mputstr(expr->preamble, temp_expr.preamble);
}
if (temp_expr.postamble == NULL) {
expr_expr = mputstr(expr_expr, temp_expr.expr);
}
else {
// make sure the postambles of the parameters are executed before the
// function call itself (needed if the template contains function calls
// with lazy or fuzzy parameters)
const string& tmp_id = val->get_temporary_id();
expr->preamble = mputprintf(expr->preamble, "%s %s(%s);\n",
temp->get_Template()->get_my_governor()->get_genname_template(my_scope).c_str(),
tmp_id.c_str(), temp_expr.expr);
expr->preamble = mputstr(expr->preamble, temp_expr.postamble);
expr_expr = mputstr(expr_expr, tmp_id.c_str());
copy_needed = false; // already copied
}
Code::free_expr(&temp_expr);
}
if (copy_needed)
expr->expr = mputprintf(expr->expr, "%s(", temp->get_Template()
->get_my_governor()->get_genname_template(my_scope).c_str());
expr->expr = mputstr(expr->expr, expr_expr);
Free(expr_expr);
if (copy_needed) expr->expr = mputc(expr->expr, ')');
}
break;
case AP_REF: {
if (param_eval != NORMAL_EVAL) FATAL_ERROR("ActualPar::generate_code()"); // syntax error should have already happened
if (copy_needed) FATAL_ERROR("ActualPar::generate_code()");
bool is_restricted_template = gen_restriction_check != TR_NONE ||
gen_post_restriction_check != TR_NONE;
bool is_template_par = false;
Type* actual_par_type = NULL;
Type* formal_par_type = NULL;
bool needs_conversion = false;
if (formal_par != NULL &&
formal_par->get_asstype() != Common::Assignment::A_PAR_TIMER &&
formal_par->get_asstype() != Common::Assignment::A_PAR_PORT) {
if (formal_par->get_asstype() == Common::Assignment::A_PAR_TEMPL_INOUT ||
formal_par->get_asstype() == Common::Assignment::A_PAR_TEMPL_OUT) {
is_template_par = true;
}
Common::Assignment *ass = ref->get_refd_assignment();
actual_par_type = ass->get_Type()->get_field_type(ref->get_subrefs(),
is_template_par ? Type::EXPECTED_TEMPLATE : Type::EXPECTED_DYNAMIC_VALUE)->
get_type_refd_last();
formal_par_type = formal_par->get_Type()->get_type_refd_last();
needs_conversion = use_runtime_2 && my_scope->get_scope_mod()->
needs_type_conv(actual_par_type, formal_par_type);
}
if (is_restricted_template || needs_conversion) {
// generate runtime check for restricted templates and/or generate
// type conversion to the formal parameter's type and back
const string& tmp_id= my_scope->get_scope_mod_gen()->get_temporary_id();
const char *tmp_id_str = tmp_id.c_str();
expression_struct ref_expr;
Code::init_expr(&ref_expr);
ref->generate_code(&ref_expr);
ref_expr.preamble = mputprintf(ref_expr.preamble, "%s& %s = %s;\n",
is_template_par ? actual_par_type->get_genname_template(my_scope).c_str() :
actual_par_type->get_genname_value(my_scope).c_str(), tmp_id_str, ref_expr.expr);
if (gen_restriction_check != TR_NONE) {
ref_expr.preamble = Template::generate_restriction_check_code(
ref_expr.preamble, tmp_id_str, gen_restriction_check);
}
if (needs_conversion) {
// create another temporary, this time of the formal parameter's type,
// containing the converted parameter
const string& tmp_id2 = my_scope->get_scope_mod_gen()->get_temporary_id();
const char *tmp_id2_str = tmp_id2.c_str();
ref_expr.preamble = mputprintf(ref_expr.preamble,
"%s %s;\n"
"if (%s.is_bound() && !%s(%s, %s)) TTCN_error(\"Values or templates "
"of types `%s' and `%s' are not compatible at run-time\");\n",
is_template_par ? formal_par_type->get_genname_template(my_scope).c_str() :
formal_par_type->get_genname_value(my_scope).c_str(), tmp_id2_str,
tmp_id_str, TypeConv::get_conv_func(actual_par_type, formal_par_type,
my_scope->get_scope_mod()).c_str(), tmp_id2_str, tmp_id_str,
actual_par_type->get_typename().c_str(), formal_par_type->get_typename().c_str());
// pass the new temporary to the function instead of the original reference
expr->expr = mputprintf(expr->expr, "%s", tmp_id2_str);
// convert the temporary's new value back to the actual parameter's type
ref_expr.postamble = mputprintf(ref_expr.postamble,
"if (%s.is_bound() && !%s(%s, %s)) TTCN_error(\"Values or templates "
"of types `%s' and `%s' are not compatible at run-time\");\n",
tmp_id2_str, TypeConv::get_conv_func(formal_par_type, actual_par_type,
my_scope->get_scope_mod()).c_str(), tmp_id_str, tmp_id2_str,
formal_par_type->get_typename().c_str(), actual_par_type->get_typename().c_str());
}
else { // is_restricted_template
expr->expr = mputprintf(expr->expr, "%s", tmp_id_str);
}
if (gen_post_restriction_check != TR_NONE) {
ref_expr.postamble = Template::generate_restriction_check_code(
ref_expr.postamble, tmp_id_str, gen_post_restriction_check);
}
// copy content of ref_expr to expr
expr->preamble = mputstr(expr->preamble, ref_expr.preamble);
expr->postamble = mputstr(expr->postamble, ref_expr.postamble);
Code::free_expr(&ref_expr);
} else {
ref->generate_code(expr);
}
break; }
case AP_DEFAULT:
if (copy_needed) FATAL_ERROR("ActualPar::generate_code()");
switch (act->selection) {
case AP_REF:
if (param_eval != NORMAL_EVAL) {
LazyFuzzyParamData::generate_code_ap_default_ref(expr, act->ref, my_scope,
param_eval == LAZY_EVAL);
} else {
act->ref->generate_code(expr);
}
break;
case AP_VALUE:
if (param_eval != NORMAL_EVAL) {
LazyFuzzyParamData::generate_code_ap_default_value(expr, act->val, my_scope,
param_eval == LAZY_EVAL);
} else {
expr->expr = mputstr(expr->expr, act->val->get_genname_own(my_scope).c_str());
}
break;
case AP_TEMPLATE:
if (param_eval != NORMAL_EVAL) {
LazyFuzzyParamData::generate_code_ap_default_ti(expr, act->temp, my_scope,
param_eval == LAZY_EVAL);
}
else if (use_runtime_2 && (formal_par == NULL ||
formal_par->get_defpar_wrapper() == Common::Type::NO_DEFPAR_WRAPPER)) {
// use the actual parameter's scope, not the formal parameter's
act->temp->set_my_scope(my_scope);
Template* temp_ = act->temp->get_Template();
if (temp_->get_templatetype() == Template::TEMPLATE_REFD ||
(temp_->get_templatetype() == Template::SPECIFIC_VALUE &&
temp_->get_specific_value()->get_valuetype() == Common::Value::V_REFD)) {
Common::Assignment* ass = temp_->get_templatetype() == Template::TEMPLATE_REFD ?
temp_->get_reference()->get_refd_assignment() :
temp_->get_specific_value()->get_reference()->get_refd_assignment();
if (ass->get_asstype() != Common::Assignment::A_FUNCTION_RVAL &&
ass->get_asstype() != Common::Assignment::A_FUNCTION_RTEMP &&
ass->get_asstype() != Common::Assignment::A_EXT_FUNCTION_RVAL &&
ass->get_asstype() != Common::Assignment::A_EXT_FUNCTION_RTEMP &&
(ass->get_asstype() != Common::Assignment::A_TEMPLATE ||
ass->get_FormalParList() == NULL)) {
// reference to a deterministic value or template, generate normally
act->temp->generate_code(expr, act->get_gen_restriction_check());
break;
}
}
// the template might contain non-deterministic function calls,
// re-generate the template's initializer code every time the
// parameter's default value is used
// (for now single expressions are not handled separately, since this
// may change the order of function calls in the default templates)
string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
expr->preamble = mputprintf(expr->preamble, "%s %s;\n",
formal_par->get_Type()->get_genname_template(my_scope).c_str(),
tmp_id.c_str());
expr->preamble = FormalPar::generate_code_defval_template(expr->preamble,
act->temp, tmp_id, act->get_gen_restriction_check());
expr->expr = mputstr(expr->expr, tmp_id.c_str());
}
else {
expr->expr = mputstr(expr->expr, act->temp->get_Template()->get_genname_own(my_scope).c_str());
}
break;
default:
FATAL_ERROR("ActualPar::generate_code()");
}
break;
default:
FATAL_ERROR("ActualPar::generate_code()");
}
}
char *ActualPar::rearrange_init_code(char *str, Common::Module* usage_mod)
{
switch (selection) {
case AP_VALUE:
str = val->rearrange_init_code(str, usage_mod);
break;
case AP_TEMPLATE:
str = temp->rearrange_init_code(str, usage_mod);
case AP_REF:
break;
case AP_DEFAULT:
str = act->rearrange_init_code_defval(str, usage_mod);
break;
default:
FATAL_ERROR("ActualPar::rearrange_init_code()");
}
return str;
}
char *ActualPar::rearrange_init_code_defval(char *str, Common::Module* usage_mod)
{
switch (selection) {
case AP_VALUE:
if (val->get_my_scope()->get_scope_mod_gen() == usage_mod) {
str = val->generate_code_init(str, val->get_lhs_name().c_str());
}
break;
case AP_TEMPLATE: {
if (use_runtime_2) {
break;
}
str = temp->rearrange_init_code(str, usage_mod);
Template *t = temp->get_Template();
if (t->get_my_scope()->get_scope_mod_gen() == usage_mod) {
Reference *dref = temp->get_DerivedRef();
if (dref) {
expression_struct expr;
Code::init_expr(&expr);
expr.expr = mputprintf(expr.expr, "%s = ", t->get_lhs_name().c_str());
dref->generate_code(&expr);
str = Code::merge_free_expr(str, &expr);
}
str = t->generate_code_init(str, t->get_lhs_name().c_str());
}
break; }
default:
FATAL_ERROR("ActualPar::rearrange_init_code_defval()");
}
return str;
}
void ActualPar::append_stringRepr(string& str) const
{
switch (selection) {
case AP_VALUE:
str += val->get_stringRepr();
break;
case AP_TEMPLATE:
temp->append_stringRepr(str);
break;
case AP_REF:
str += ref->get_dispname();
break;
case AP_DEFAULT:
str += '-';
break;
default:
str += "<erroneous actual parameter>";
}
}
void ActualPar::dump(unsigned level) const
{
switch (selection) {
case AP_VALUE:
DEBUG(level, "actual parameter: value");
val->dump(level + 1);
break;
case AP_TEMPLATE:
DEBUG(level, "actual parameter: template");
temp->dump(level + 1);
break;
case AP_REF:
DEBUG(level, "actual parameter: reference");
ref->dump(level + 1);
break;
case AP_DEFAULT:
DEBUG(level, "actual parameter: default");
break;
default:
DEBUG(level, "actual parameter: erroneous");
}
}
Location* ActualPar::get_location() const
{
switch (selection) {
case AP_VALUE:
return val;
case AP_TEMPLATE:
return temp;
case AP_REF:
return ref;
case AP_DEFAULT:
return act->get_location();
default:
FATAL_ERROR("ActualPar::get_location()");
}
}
// =================================
// ===== ActualParList
// =================================
ActualParList::ActualParList(const ActualParList& p)
: Node(p)
{
size_t nof_pars = p.params.size();
for (size_t i = 0; i < nof_pars; i++) params.add(p.params[i]->clone());
}
ActualParList::~ActualParList()
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) delete params[i];
params.clear();
}
ActualParList *ActualParList::clone() const
{
return new ActualParList(*this);
}
void ActualParList::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
size_t nof_pars = params.size();
for(size_t i = 0; i < nof_pars; i++)
params[i]->set_fullname(p_fullname +
".<parameter" + Int2string(i + 1) + ">");
}
void ActualParList::set_my_scope(Scope *p_scope)
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) params[i]->set_my_scope(p_scope);
}
void ActualParList::chk_recursions(ReferenceChain& refch)
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++)
params[i]->chk_recursions(refch);
}
void ActualParList::chk_immutability() {
size_t num = this->get_nof_pars();
for (size_t i = 0; i < num; ++i) {
const Ttcn::ActualPar *ap = this->get_par(i);
deeper:
switch (ap->get_selection()) {
case ActualPar::AP_ERROR:
break;
case ActualPar::AP_VALUE: ///< "in" value parameter
ap->get_Value()->chk_expr_immutability();
break;
case ActualPar::AP_TEMPLATE: ///< "in" template parameter
ap->get_TemplateInstance()->chk_immutability();
break;
case ActualPar::AP_REF: ///< out/inout value or template parameter
ap->get_Ref()->chk_immutability();
break;
case ActualPar::AP_DEFAULT: { ///< created from the default value of a formal parameter
// TODO: test!
ap = ap->get_ActualPar();
goto deeper;
break;
}
// no default
} // switch actual par selection
} // next
}
void ActualParList::chk_ctor_defpar(bool default_ctor, bool in_base_call)
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) {
params[i]->chk_ctor_defpar(default_ctor, in_base_call);
}
}
void ActualParList::chk_class_member(ClassTypeBody* p_class)
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) {
params[i]->chk_class_member(p_class);
}
}
void ActualParList::set_gen_class_defpar_prefix()
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) {
params[i]->set_gen_class_defpar_prefix();
}
}
void ActualParList::set_gen_class_base_call_postfix()
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) {
params[i]->set_gen_class_base_call_postfix();
}
}
void ActualParList::generate_code_noalias(expression_struct *expr, FormalParList *p_fpl)
{
size_t nof_pars = params.size();
for (size_t i = 0; i < nof_pars; i++) {
if (i > 0) expr->expr = mputstr(expr->expr, ", ");
params[i]->generate_code(expr, false, p_fpl != NULL ? p_fpl->get_fp_byIndex(i) : NULL);
}
}
void ActualParList::generate_code_alias(expression_struct *expr,
FormalParList *p_fpl, Type *p_comptype, bool p_compself)
{
size_t nof_pars = params.size();
// collect all value and template definitions that are passed by reference
map<Common::Assignment*, void> value_refs, template_refs;
for (size_t i = 0; i < nof_pars; i++) {
ActualPar *par = params[i];
if (par->get_selection() == ActualPar::AP_DEFAULT)
par = par->get_ActualPar();
if (par->get_selection() == ActualPar::AP_REF) {
Common::Assignment *ass = par->get_Ref()->get_refd_assignment();
switch (ass->get_asstype()) {
case Common::Assignment::A_VAR:
case Common::Assignment::A_EXCEPTION:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT:
if (!value_refs.has_key(ass)) value_refs.add(ass, 0);
break;
case Common::Assignment::A_VAR_TEMPLATE:
case Common::Assignment::A_PAR_TEMPL_IN:
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
if (!template_refs.has_key(ass)) template_refs.add(ass, 0);
default:
break;
}
}
}
// walk through the parameter list and generate the code
// add an extra copy constructor call to the referenced value and template
// parameters if the referred definition is also passed by reference to
// another parameter
bool all_in_params_shadowed = use_runtime_2 && p_fpl != NULL &&
p_fpl->get_my_def()->get_asstype() == Common::Assignment::A_ALTSTEP;
for (size_t i = 0; i < nof_pars; i++) {
if (i > 0) expr->expr = mputstr(expr->expr, ", ");
ActualPar *par = params[i];
bool copy_needed = false;
// the copy constructor call is not needed if the parameter is copied
// into a shadow object in the body of the called function
bool shadowed = false;
if (p_fpl != NULL) {
switch (p_fpl->get_fp_byIndex(i)->get_asstype()) {
case Common::Assignment::A_PAR_VAL:
case Common::Assignment::A_PAR_VAL_IN:
case Common::Assignment::A_PAR_TEMPL_IN:
// all 'in' parameters are shadowed in altsteps in RT2, otherwise an
// 'in' parameter is shadowed if it is used as lvalue
shadowed = all_in_params_shadowed ? true :
p_fpl->get_fp_byIndex(i)->get_used_as_lvalue();
break;
default:
break;
}
}
if (!shadowed) {
switch (par->get_selection()) {
case ActualPar::AP_VALUE: {
Value *v = par->get_Value();
if (v->get_valuetype() == Value::V_REFD) {
Common::Assignment *t_ass =
v->get_reference()->get_refd_assignment();
if (value_refs.has_key(t_ass)) {
// a reference to the same variable is also passed to the called
// definition
copy_needed = true;
} else if (p_comptype || p_compself) {
// the called definition has a 'runs on' clause so it can access
// component variables
switch (t_ass->get_asstype()) {
case Common::Assignment::A_PAR_VAL_OUT:
case Common::Assignment::A_PAR_VAL_INOUT:
// the parameter may be an alias of a component variable
copy_needed = true;
break;
case Common::Assignment::A_VAR:
// copy is needed if t_ass is a component variable that is
// visible by the called definition
if (!t_ass->is_local()) copy_needed = true;
/** \todo component type compatibility: check whether t_ass is
* visible from p_comptype (otherwise copy is not needed) */
default:
break;
}
}
}
break; }
case ActualPar::AP_TEMPLATE: {
TemplateInstance *ti = par->get_TemplateInstance();
if (!ti->get_DerivedRef()) {
Template *t = ti->get_Template();
if (t->get_templatetype() == Template::TEMPLATE_REFD) {
Common::Assignment *t_ass =
t->get_reference()->get_refd_assignment();
if (template_refs.has_key(t_ass)) {
// a reference to the same variable is also passed to the called
// definition
copy_needed = true;
} else if (p_comptype || p_compself) {
// the called definition has a 'runs on' clause so it can access
// component variables
switch (t_ass->get_asstype()) {
case Common::Assignment::A_PAR_TEMPL_OUT:
case Common::Assignment::A_PAR_TEMPL_INOUT:
// the parameter may be an alias of a component variable
copy_needed = true;
break;
case Common::Assignment::A_VAR_TEMPLATE:
// copy is needed if t_ass is a component variable that is
// visible by the called definition
if (!t_ass->is_local()) copy_needed = true;
/** \todo component type compatibility: check whether t_ass is
* visible from p_comptype (otherwise copy is not needed) */
default:
break;
}
}
}
} }
default:
break;
}
}
if (use_runtime_2 && ActualPar::AP_REF == par->get_selection()) {
// if the parameter references an element of a record of/set of, then
// the record of object needs to know, so it doesn't delete the referenced
// element
Reference* ref = par->get_Ref();
FieldOrArrayRefs* subrefs = ref->get_subrefs();
if (subrefs != NULL) {
Common::Assignment* ass = ref->get_refd_assignment();
size_t ref_i;
for (ref_i = 0; ref_i < subrefs->get_nof_refs(); ++ref_i) {
FieldOrArrayRef* subref = subrefs->get_ref(ref_i);
if (FieldOrArrayRef::ARRAY_REF == subref->get_type()) {
// set the referenced index in each array in the subrefs
expression_struct array_expr;
Code::init_expr(&array_expr);
// the array object's name contains the reference, followed by
// the subrefs before the current array ref
array_expr.expr = mcopystr(LazyFuzzyParamData::in_lazy_or_fuzzy() ?
LazyFuzzyParamData::add_ref_genname(ass, ref->get_my_scope()).c_str() :
ass->get_genname_from_scope(ref->get_my_scope()).c_str());
if (ref_i > 0) {
subrefs->generate_code(&array_expr, ass, ref->get_my_scope(), false, ref_i);
}
expression_struct index_expr;
Code::init_expr(&index_expr);
subrefs->get_ref(ref_i)->get_val()->generate_code_expr(&index_expr);
// insert any preambles the array object or the index might have
if (array_expr.preamble != NULL) {
expr->preamble = mputstr(expr->preamble, array_expr.preamble);
expr->postamble = mputstr(expr->postamble, array_expr.preamble);
}
if (index_expr.preamble != NULL) {
expr->preamble = mputstr(expr->preamble, index_expr.preamble);
expr->postamble = mputstr(expr->postamble, index_expr.preamble);
}
// let the array object know that the index is referenced before
// calling the function, and let it know that it's now longer
// referenced after the function call (this is done with the help
// of the RefdIndexHandler's constructor and destructor)
string tmp_id = ref->get_my_scope()->get_scope_mod_gen()->get_temporary_id();
expr->preamble = mputprintf(expr->preamble,
"RefdIndexHandler %s(&%s, %s);\n",
tmp_id.c_str(), array_expr.expr, index_expr.expr);
// insert any postambles the array object or the index might have
if (array_expr.postamble != NULL) {
expr->preamble = mputstr(expr->preamble, array_expr.postamble);
expr->postamble = mputstr(expr->postamble, array_expr.postamble);
}
if (index_expr.postamble != NULL) {
expr->preamble = mputstr(expr->preamble, index_expr.postamble);
expr->postamble = mputstr(expr->postamble, index_expr.postamble);
}
Code::free_expr(&array_expr);
Code::free_expr(&index_expr);
} // if (FieldOrArrayRef::ARRAY_REF == subref->get_type())
} // for cycle
} // if (subrefs != NULL)
} // if (ActualPar::AP_REF == par->get_selection())
par->generate_code(expr, copy_needed, p_fpl != NULL ? p_fpl->get_fp_byIndex(i) : NULL);
}
value_refs.clear();
template_refs.clear();
}
char *ActualParList::rearrange_init_code(char *str, Common::Module* usage_mod)
{
for (size_t i = 0; i < params.size(); i++)
str = params[i]->rearrange_init_code(str, usage_mod);
return str;
}
void ActualParList::dump(unsigned level) const
{
DEBUG(level, "actual parameter list: %lu parameters",
static_cast<unsigned long>( params.size()));
for (size_t i = 0; i < params.size(); i++)
params[i]->dump(level + 1);
}
}