Commit 94e477a2 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

optimized redirects with '@decoded' to use decmatch result (artf768988)



Change-Id: If2d455116506ff5d625259e618852f22adc72004
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 6f5f745f
......@@ -5756,7 +5756,7 @@ bool Type::chk_this_template_Str(Template *t, namedbool implicit_omit,
target->error("Type of template instance cannot be determined");
break;
}
if (target->get_Type() != NULL) {
if (target->get_Type() != NULL && target_type->is_ref()) {
target_type = target_type->get_type_refd();
}
self_ref = target_type->chk_this_template_generic(
......
This diff is collapsed.
......@@ -1025,12 +1025,13 @@ namespace Ttcn {
/** Sets the code section selector of all embedded values and templates
* to \a p_code_section. */
void set_code_section(GovernedSimple::code_section_t p_code_section);
void generate_code(expression_struct_t *expr);
void generate_code(expression_struct_t *expr, TemplateInstance* matched_ti,
bool is_out);
/** generates a new redirect class, inherited from the signature type's
* original redirect class, which extends its functionality to also
* decode the redirected parameters that have the '@decoded' modifier */
char* generate_code_decoded(char* str, Type* sig_type, const char* tmp_id,
bool is_out);
char* generate_code_decoded(char* str, TemplateInstance* matched_ti,
const char* tmp_id, bool is_out);
/** returns true if at least one of the parameter redirects has the
* '@decoded' modifier */
bool has_decoded_modifier() const;
......@@ -1115,9 +1116,15 @@ namespace Ttcn {
/** Generates code for the value redirect in the specified expression
* structure. A new class is generated for every value redirect, which
* handles the redirecting.
* @param matched_ti the template instance used for matching the redirected
* value
* @param base_class_prefix the namespace and/or class prefix of the
* base value redirect class of the appropriate type */
void generate_code(expression_struct* expr, string base_class_prefix);
void generate_code(expression_struct* expr, TemplateInstance* matched_ti,
string base_class_prefix);
/** returns true if at least one of the value redirects has the
* '@decoded' modifier*/
bool has_decoded_modifier() const;
};
/**
......
......@@ -1225,6 +1225,15 @@ namespace Ttcn {
FATAL_ERROR("Template::get_namedtemp_byIndex()");
return u.named_templates->get_nt_byIndex(n);
}
NamedTemplate* Template::get_namedtemp_byName(const Identifier& name) const
{
if (templatetype != NAMED_TEMPLATE_LIST) {
FATAL_ERROR("Template::get_namedtemp_byName()");
}
return u.named_templates->has_nt_withName(name) ?
u.named_templates->get_nt_byName(name) : NULL;
}
Template *Template::get_all_from() const
{
......@@ -4519,38 +4528,50 @@ compile_time:
Type* target_type = u.dec_match.target->get_expr_governor(
Type::EXPECTED_TEMPLATE)->get_type_refd_last();
// use the name of the type at the end of the reference chain for logging
string type_name;
const char* type_name_ptr = target_type->get_typename_builtin(
target_type->get_typetype_ttcn3());
if (type_name_ptr == NULL) {
type_name_ptr = target_type->get_type_refd_last()->get_dispname().c_str();
type_name = target_type->get_type_refd_last()->get_dispname();
}
else {
type_name = type_name_ptr;
}
// copy the character pointer returned by Type::get_dispname() as it might
// change before its use
char* type_name = mcopystr(type_name_ptr);
str = mputprintf(str,
"class Dec_Match_%s : public Dec_Match_Interface {\n"
// store the decoding target as a member, since both functions use it
// store the decoding target as a member, since 2 functions use it
"%s target;\n"
// store the decoding result from the last successful match() call
"%s* dec_val;\n"
"public:\n"
"Dec_Match_%s(%s p_target): target(p_target) { }\n"
"Dec_Match_%s(%s p_target): target(p_target), dec_val(NULL) { }\n"
"~Dec_Match_%s() { if (dec_val != NULL) delete dec_val; }\n"
// called when matching, the buffer parameter contains the string to be matched
"virtual boolean match(TTCN_Buffer& buff) const\n"
"virtual boolean match(TTCN_Buffer& buff)\n"
"{\n"
"%s val;\n"
"if (dec_val != NULL) delete dec_val;\n"
"dec_val = new %s;\n"
// decode the value
"val.decode(%s_descr_, buff, TTCN_EncDec::CT_%s);\n"
"dec_val->decode(%s_descr_, buff, TTCN_EncDec::CT_%s);\n"
"boolean ret_val;\n"
// make sure no errors occurred (these already displayed warnings during
// decoding)
"if (TTCN_EncDec::get_last_error_type() != TTCN_EncDec::ET_NONE) "
"return FALSE;\n"
"ret_val = FALSE;\n"
// make sure the buffer is empty after decoding, display a warning otherwise
"if (buff.get_read_len() != 0) {\n"
"else if (buff.get_read_len() != 0) {\n"
"TTCN_warning(\"Decoded content matching failed, because the buffer was not "
"empty after decoding. Remaining octets: %%d.\", (int)buff.get_read_len());\n"
"return FALSE;\n"
"ret_val = FALSE;\n"
"}\n"
// finally, match the decoded value against the target template
"return target.match(val%s);\n"
"else ret_val = target.match(*dec_val%s);\n"
// delete the decoded value if matching was not successful
"if (!ret_val) {\n"
"delete dec_val;\n"
"dec_val = NULL;\n"
"}\n"
"return ret_val;\n"
"}\n"
"virtual void log() const\n"
"{\n"
......@@ -4558,16 +4579,19 @@ compile_time:
"TTCN_Logger::log_event_str(\"%s: \");\n"
"target.log();\n"
"}\n"
// retrieves the decoding result from the last successful matching
// (used for optimizing decoded value and parameter redirects)
"void* get_dec_res() const { return dec_val; }\n"
"};\n"
"%s.set_type(DECODE_MATCH);\n"
"{\n", class_tmp_id.c_str(),
target_type->get_genname_template(my_scope).c_str(), class_tmp_id.c_str(),
target_type->get_genname_template(my_scope).c_str(),
target_type->get_genname_value(my_scope).c_str(), class_tmp_id.c_str(),
target_type->get_genname_template(my_scope).c_str(), class_tmp_id.c_str(),
target_type->get_genname_value(my_scope).c_str(),
target_type->get_genname_typedescriptor(my_scope).c_str(),
target_type->get_coding(false).c_str(),
omit_in_value_list ? ", TRUE" : "", type_name, name);
Free(type_name);
omit_in_value_list ? ", TRUE" : "", type_name.c_str(), name);
// generate the decoding target into a temporary
string target_tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
......@@ -4988,7 +5012,7 @@ compile_time:
TemplateInstance::TemplateInstance(Type *p_type, Ref_base *p_ref,
Template *p_body) : Node(), Location(), type(p_type),
derived_reference(p_ref), template_body(p_body)
derived_reference(p_ref), template_body(p_body), last_gen_expr(NULL)
{
if (!p_body) FATAL_ERROR("TemplateInstance::TemplateInstance()");
if (type) type->set_ownertype(Type::OT_TEMPLATE_INST, this);
......@@ -4999,6 +5023,7 @@ compile_time:
delete type;
delete derived_reference;
delete template_body;
Free(last_gen_expr);
}
void TemplateInstance::release()
......@@ -5252,8 +5277,9 @@ compile_time:
}
void TemplateInstance::generate_code(expression_struct *expr,
template_restriction_t template_restriction)
template_restriction_t template_restriction, bool has_decoded_redirect)
{
size_t start_pos = mstrlen(expr->expr);
if (derived_reference) {
// preserve the target expression
char *expr_backup = expr->expr;
......@@ -5280,7 +5306,31 @@ compile_time:
// restore the target expression append the name of the temporary
// variable to it
expr->expr = mputstr(expr_backup, tmp_id_str);
} else template_body->generate_code_expr(expr, template_restriction);
} else {
char *expr_backup;
if (has_decoded_redirect) {
// preserve the target expression
expr_backup = expr->expr;
// reset the space for the target expression
expr->expr = NULL;
}
template_body->generate_code_expr(expr, template_restriction);
if (has_decoded_redirect) {
// create a temporary variable and move the template's initialization code
// after it
const string& tmp_id = template_body->get_temporary_id();
const char *tmp_id_str = tmp_id.c_str();
expr->preamble = mputprintf(expr->preamble, "%s %s(%s);\n",
template_body->get_my_governor()->get_genname_template(
template_body->get_my_scope()).c_str(), tmp_id_str, expr->expr);
Free(expr->expr);
// restore the target expression and append the name of the temporary
// variable to it
expr->expr = mputstr(expr_backup, tmp_id_str);
}
}
size_t end_pos = mstrlen(expr->expr);
last_gen_expr = mcopystrn(expr->expr + start_pos, end_pos - start_pos);
}
char *TemplateInstance::rearrange_init_code(char *str, Common::Module* usage_mod)
......
......@@ -285,6 +285,7 @@ namespace Ttcn {
size_t get_nof_comps() const;
Template *get_temp_byIndex(size_t n) const;
NamedTemplate *get_namedtemp_byIndex(size_t n) const;
NamedTemplate* get_namedtemp_byName(const Identifier& name) const;
IndexedTemplate *get_indexedtemp_byIndex(size_t n) const;
Template *get_all_from() const;
/** Returns the number of elements in a VALUE_LIST. The elements of
......@@ -517,6 +518,8 @@ namespace Ttcn {
Type *type; // type before the colon, may be null
Ref_base *derived_reference; // base template, may be null
Template *template_body; // must not be null
char* last_gen_expr; // last expression generated from this template instance
// (used if this template needs to be used multiple times)
/** Copy constructor disabled. */
TemplateInstance(const TemplateInstance& p);
......@@ -539,6 +542,7 @@ namespace Ttcn {
Type* get_Type() const { return type; }
Ref_base* get_DerivedRef() const { return derived_reference; }
Template* get_Template() const { return template_body; }
char* get_last_gen_expr() const { return last_gen_expr; }
// it can return null pointer
Def_Template* get_Referenced_Base_Template();
......@@ -577,7 +581,8 @@ namespace Ttcn {
void set_code_section(GovernedSimple::code_section_t p_code_section);
bool needs_temp_ref();
void generate_code(expression_struct *expr,
template_restriction_t template_restriction = TR_NONE);
template_restriction_t template_restriction = TR_NONE,
bool has_decoded_redirect = false);
/** Appends the initialization sequence of the referred templates
* and their default values to \a str. Only templates from module
* \a usage_mod are considered. */
......
......@@ -1095,8 +1095,14 @@ public:
* functions when the template object's match() or log() functions are called. */
class Dec_Match_Interface {
public:
virtual boolean match(TTCN_Buffer&) const = 0;
virtual boolean match(TTCN_Buffer&) = 0;
virtual void log() const = 0;
/** this returns the decoding result of the last successfully matched value,
* which may be used by value and parameter redirect classes for optimization
* (so they don't have to decode the same value again)
* the function returns a void pointer (since the decoding could result in a
* value of any type), which is converted to the required type when used */
virtual void* get_dec_res() const = 0;
virtual ~Dec_Match_Interface() {}
};
......
......@@ -1827,6 +1827,15 @@ void BITSTRING_template::set_decmatch(Dec_Match_Interface* new_instance)
dec_match->instance = new_instance;
}
void* BITSTRING_template::get_decmatch_dec_res() const
{
if (template_selection != DECODE_MATCH) {
TTCN_error("Retrieving the decoding result of a non-decmatch bitstring "
"template.");
}
return dec_match->instance->get_dec_res();
}
static const char patterns[] = { '0', '1', '?', '*' };
void BITSTRING_template::log() const
......
......@@ -299,6 +299,8 @@ public:
BITSTRING_template& list_item(unsigned int list_index);
void set_decmatch(Dec_Match_Interface* new_instance);
void* get_decmatch_dec_res() const;
void log() const;
void log_match(const BITSTRING& match_value, boolean legacy = FALSE) const;
......
......@@ -2445,6 +2445,15 @@ void CHARSTRING_template::set_decmatch(Dec_Match_Interface* new_instance)
dec_match->coding = CharCoding::UTF_8;
}
void* CHARSTRING_template::get_decmatch_dec_res() const
{
if (template_selection != DECODE_MATCH) {
TTCN_error("Retrieving the decoding result of a non-decmatch charstring "
"template.");
}
return dec_match->instance->get_dec_res();
}
void CHARSTRING_template::log_pattern(int n_chars, const char *chars_ptr)
{
TTCN_Logger::log_event_str("pattern \"");
......
......@@ -430,6 +430,8 @@ public:
void set_max(const CHARSTRING& max_value);
void set_decmatch(Dec_Match_Interface* new_instance);
void* get_decmatch_dec_res() const;
void log() const;
void log_match(const CHARSTRING& match_value, boolean legacy = FALSE) const;
......
......@@ -1713,6 +1713,15 @@ void HEXSTRING_template::set_decmatch(Dec_Match_Interface* new_instance)
dec_match->instance = new_instance;
}
void* HEXSTRING_template::get_decmatch_dec_res() const
{
if (template_selection != DECODE_MATCH) {
TTCN_error("Retrieving the decoding result of a non-decmatch hexstring "
"template.");
}
return dec_match->instance->get_dec_res();
}
void HEXSTRING_template::log() const
{
switch (template_selection) {
......
......@@ -253,6 +253,8 @@ public:
HEXSTRING_template& list_item(unsigned int list_index);
void set_decmatch(Dec_Match_Interface* new_instance);
void* get_decmatch_dec_res() const;
void log() const;
void log_match(const HEXSTRING& match_value, boolean legacy = FALSE) const;
......
......@@ -1949,6 +1949,15 @@ void OCTETSTRING_template::set_decmatch(Dec_Match_Interface* new_instance)
dec_match->instance = new_instance;
}
void* OCTETSTRING_template::get_decmatch_dec_res() const
{
if (template_selection != DECODE_MATCH) {
TTCN_error("Retrieving the decoding result of a non-decmatch octetstring "
"template.");
}
return dec_match->instance->get_dec_res();
}
void OCTETSTRING_template::log() const
{
switch (template_selection) {
......
......@@ -284,6 +284,8 @@ public:
OCTETSTRING_template& list_item(unsigned int list_index);
void set_decmatch(Dec_Match_Interface* new_instance);
void* get_decmatch_dec_res() const;
void log() const;
void log_match(const OCTETSTRING& match_value, boolean legacy = FALSE) const;
......
......@@ -4205,6 +4205,24 @@ void UNIVERSAL_CHARSTRING_template::set_decmatch(Dec_Match_Interface* new_instan
dec_match->coding = new_coding;
}
void* UNIVERSAL_CHARSTRING_template::get_decmatch_dec_res() const
{
if (template_selection != DECODE_MATCH) {
TTCN_error("Retrieving the decoding result of a non-decmatch universal "
"charstring template.");
}
return dec_match->instance->get_dec_res();
}
CharCoding::CharCodingType UNIVERSAL_CHARSTRING_template::get_decmatch_str_enc() const
{
if (template_selection != DECODE_MATCH) {
TTCN_error("Retrieving the encoding format of a non-decmatch universal "
"charstring template.");
}
return dec_match->coding;
}
void UNIVERSAL_CHARSTRING_template::log() const
{
switch (template_selection) {
......
......@@ -610,6 +610,9 @@ public:
void set_max(const UNIVERSAL_CHARSTRING& max_value);
void set_decmatch(Dec_Match_Interface* new_instance, const char* coding_str = NULL);
void* get_decmatch_dec_res() const;
CharCoding::CharCodingType get_decmatch_str_enc() const;
void log() const;
void log_match(const UNIVERSAL_CHARSTRING& match_value, boolean legacy = FALSE) const;
......
......@@ -29,6 +29,10 @@ with {
variant (str) "untagged";
}
type record MyRecord3 {
record of MyRecord2 elems
}
type octetstring MyOctetstring with { encode "RAW"; }
signature MyProc(in integer Par1,inout charstring Par2,out float Par3)
......@@ -42,7 +46,7 @@ signature MyProc5(in integer A, in integer B)
return float exception(charstring, MyRecord);
signature s_StopPTC();
signature MyProc6(inout integer I);
signature MyProc7(inout universal charstring x) return MyRecord2 exception(MyRecord2);
signature MyProc7(inout universal charstring x) return MyRecord2 exception(MyRecord2, MyRecord3);
template s_StopPTC StopPTC := { }
......@@ -102,6 +106,10 @@ template address MyAddress(integer p1,integer p2) := {
a2:=p2
}
template MyProc7 MyProc7TemplatePard(template universal charstring p) := {
x := p
}
type port PortAddress procedure {
inout MyProc,s_StopPTC;
in MyProc2;
......@@ -713,16 +721,18 @@ testcase tc_MultiValueRedirect() runs on ProcComponent4 {
setverdict(pass);
}
// test for decoded parameter and value redirects
// tests for decoded parameter and value redirects
// (in these tests the value and parameter redirects with the '@decoded' modifier
// are optimized to reuse the decoding result in the 'decmatch' template instead of
// decoding the same string twice)
function DecodedRedirect_behav(in MyRecord2 val, in universal charstring reply_val) runs on ProcComponent4 {
timer tmr := 1.0;
var universal charstring val_enc := encvalue_unichar(val);
var MyRecord2 redir;
// testing parameter redirect in getcall:
tmr.start;
alt {
[] Port4.getcall(MyProc7: { val_enc }) -> param (redir := @decoded x) {
[] Port4.getcall(MyProc7: { decmatch("UTF-8") MyRecord2: val }) -> param (redir := @decoded x) {
if (redir != val) { setverdict(fail, "Getcall parameter redirect failed: ", redir); }
else {
var MyRecord2 reply_rec := { num := -1, str := reply_val };
......@@ -741,18 +751,18 @@ testcase tc_DecodedRedirect() runs on ProcComponent4 {
var MyRecord2 val := { num := 4, str := "stuff" };
// use this string instead of an actual encoded value for reply/getreply and raise/catch,
// because finding a value that can be encoded with UTF-16 or UTF-32 is difficult
// (after this string is decoded into an octetstring, only the octetstring's length is be matched)
// (after this string is decoded into an octetstring, only the octetstring's length is matched)
var universal charstring reply_val := "payload";
ct.start(DecodedRedirect_behav(val, reply_val));
var universal charstring val_enc := encvalue_unichar(val);
var MyOctetstring redir[3];
// these encoding strings are not known at compile-time (the code generated for them is different)
var charstring str_enc16 := "UTF-16", str_enc32 := "UTF-32LE";
var charstring str_enc8 := "UTF-8", str_enc16 := "UTF-16", str_enc32 := "UTF-32LE";
// testing parameter and (return) value redirect in getreply:
Port4.call(MyProc7: { val_enc }, 1.0) {
[] Port4.getreply(MyProc7: { decmatch(str_enc16) MyOctetstring: ? length (16) }
value MyRecord2: { num := ?, str := decmatch("UTF-8") MyOctetstring: ? length (7) })
value MyRecord2: { num := ?, str := decmatch(str_enc8) MyOctetstring: ? length (7) })
-> value (redir[0] := @decoded("UTF-8") str)
param (redir[1] := @decoded(str_enc16) x) {
if (redir[0] != unichar2oct(reply_val, "UTF-8")) {
......@@ -769,23 +779,118 @@ testcase tc_DecodedRedirect() runs on ProcComponent4 {
setverdict(fail, "Invalid getreply parameter.");
}
[] Port4.catch(MyProc7, MyRecord2: ?) {
setverdict(fail, "Exception caught in getreply test.");
setverdict(fail, "Exception (MyRecord2) caught in getreply test.");
}
[] Port4.catch(MyProc7, MyRecord3: ?) {
setverdict(fail, "Exception (MyRecord3) caught in getreply test.");
}
[] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); }
}
// testing value (exception) redirect in catch:
// testing (exception) value redirect in catch:
timer tmr := 1.0;
tmr.start;
alt {
[] Port4.catch(MyProc7, MyRecord2: { num := ?, str := decmatch(str_enc32) MyOctetstring: ? length(32) })
[] Port4.catch(MyProc7, MyRecord2: { num := ?, str := decmatch("UTF-32LE") MyOctetstring: ? length(32) })
-> value (redir[2] := @decoded(str_enc32) str) {
if (redir[2] != unichar2oct(reply_val, str_enc32)) {
setverdict(fail, "Exception value redirect failed: ", redir[2]);
}
}
[] Port4.catch(MyProc7, MyRecord2: ?) {
setverdict(fail, "Invalid exception caught.");
setverdict(fail, "Invalid exception value caught.");
}
[] Port4.catch(MyProc7, MyRecord3: ?) {
setverdict(fail, "Invalid type of exception caught.");
}
[] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) {
setverdict(fail, "Reply received in exception test.");
}
[] tmr.timeout { setverdict(fail, "Exception test timed out."); }
}
setverdict(pass);
}
// additional tests for decoded parameter and value redirects
// (in these tests the 'decmatch' templates cannot be identified at compile-time,
// so the decision of whether to decode the redirected value again or use the
// decoding result in the matched template is made at runtime)
function DecodedRedirect2_behav(in MyRecord2 val) runs on ProcComponent4 {
timer tmr := 1.0;
var MyRecord2 redir;
var template MyProc7 vt_proc7 := { x := decmatch MyRecord2: val };
// testing parameter redirect in getcall:
tmr.start;
alt {
[] Port4.getcall(vt_proc7) -> param (redir := @decoded x) {
if (redir != val) { setverdict(fail, "Getcall parameter redirect failed: ", redir); }
else {
var universal charstring val_enc := encvalue_unichar(val);
var MyRecord2 reply_val := { num := val.num, str := val_enc };
var MyRecord3 raise_val := { elems := { reply_val } };
Port4.reply(MyProc7: { val_enc } value reply_val);
Port4.raise(MyProc7, raise_val);
}
}
[] Port4.getcall(MyProc7: { ? }) { setverdict(fail, "Invalid getcall parameter."); }
[] tmr.timeout { setverdict(fail, "Getcall timed out."); }
}
}
testcase tc_DecodedRedirect2() runs on ProcComponent4 {
var ProcComponent4 ct := ProcComponent4.create;
connect(ct:Port4, mtc:Port4);
var MyRecord2 val := { num := 4, str := "stuff" };
ct.start(DecodedRedirect2_behav(val));
var universal charstring val_enc := encvalue_unichar(val);
var MyRecord2 redir[3];
var template MyRecord2 vt_rec2 := { num := ?, str := decmatch MyRecord2: val };
var charstring str_enc8 := "UTF-8";
// testing parameter and (return) value redirect in getreply:
Port4.call(MyProc7: { val_enc }, 1.0) {
[] Port4.getreply(MyProc7TemplatePard(decmatch MyRecord2: val)
value modifies vt_rec2 := { num := val.num })
-> value (redir[0] := @decoded str)
param (redir[1] := @decoded(str_enc8) x) {
if (redir[0] != val) {
setverdict(fail, "Getreply parameter redirect failed: ", redir[0]);
}
if (redir[1] != val) {
setverdict(fail, "Getreply value redirect failed: ", redir[1]);
}
}
[] Port4.getreply(MyProc7TemplatePard(decmatch MyRecord2: val) value MyRecord2: ?) {
setverdict(fail, "Invalid getreply return value.");
}
[] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) {
setverdict(fail, "Invalid getreply parameter.");
}
[] Port4.catch(MyProc7, MyRecord2: ?) {
setverdict(fail, "Exception (MyRecord2) caught in getreply test.");
}
[] Port4.catch(MyProc7, MyRecord3: ?) {
setverdict(fail, "Exception (MyRecord3) caught in getreply test.");
}
[] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); }
}
// testing (exception) value redirect in catch:
timer tmr := 1.0;
tmr.start;
alt {
[] Port4.catch(MyProc7, MyRecord3: { elems := { vt_rec2 } })
-> value (redir[2] := @decoded(str_enc8) elems[0].str) {
if (redir[2] != val) {
setverdict(fail, "Exception value redirect failed: ", redir[2]);
}
}
[] Port4.catch(MyProc7, MyRecord3: ?) {
setverdict(fail, "Invalid exception value caught.");
}
[] Port4.catch(MyProc7, MyRecord2: ?) {
setverdict(fail, "Invalid type of exception caught.");