Commit 6104e7e8 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

The compiler now generates an include statement if the modules containing...


The compiler now generates an include statement if the modules containing custom or PER coders is not imported in the modules containing their usage (bug 500334)

Change-Id: I7a4a0443a482193f9a1ab79c6ffe663eaad92935
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 33e3a1a3
......@@ -177,7 +177,8 @@ namespace Common {
// run delayed Type::chk_coding() calls
if (!delayed_type_enc_v.empty()) {
for (size_t i = 0; i < delayed_type_enc_v.size(); ++i) {
delayed_type_enc_v[i]->t->chk_coding(delayed_type_enc_v[i]->enc, true);
delayed_type_enc_v[i]->t->chk_coding(delayed_type_enc_v[i]->enc,
delayed_type_enc_v[i]->mod, true);
delete delayed_type_enc_v[i];
}
delayed_type_enc_v.clear();
......@@ -303,10 +304,11 @@ namespace Common {
}
}
void Modules::delay_type_encode_check(Type* p_type, bool p_encode)
void Modules::delay_type_encode_check(Type* p_type, Module* p_module, bool p_encode)
{
for (size_t i = 0; i < delayed_type_enc_v.size(); ++i) {
if (delayed_type_enc_v[i]->t == p_type &&
delayed_type_enc_v[i]->mod == p_module &&
delayed_type_enc_v[i]->enc == p_encode) {
// it's already in the list of delayed checks
return;
......@@ -314,6 +316,7 @@ namespace Common {
}
type_enc_t* elem = new type_enc_t;
elem->t = p_type;
elem->mod = p_module;
elem->enc = p_encode;
delayed_type_enc_v.add(elem);
}
......
......@@ -100,6 +100,7 @@ namespace Common {
/** Contains info needed for delayed type encoding checks */
struct type_enc_t {
Type* t;
Module* mod;
bool enc;
};
static vector<type_enc_t> delayed_type_enc_v;
......@@ -152,12 +153,12 @@ namespace Common {
* info related to each type */
void generate_json_schema(JSON_Tokenizer& json, map<Type*, JSON_Tokenizer>& json_refs);
/** Called if a Type::chk_coding() call could not be resolved (because the
* needed custom coding function was not found yet, but it might be among
* the functions that have not been checked yet).
/** Called if a Type::chk_coding() call could not be resolved (the check for
* custom and PER coding requires the checks of all external functions to
* be completed).
* This stores the info needed to call the function again after everything
* else has been checked. */
static void delay_type_encode_check(Type* p_type, bool p_encode);
static void delay_type_encode_check(Type* p_type, Module* p_module, bool p_encode);
};
/**
......
......@@ -4428,16 +4428,32 @@ namespace Common {
}
}
void Type::chk_coding(bool encode, bool delayed /* = false */) {
void Type::chk_coding(bool encode, Module* usage_mod, bool delayed /* = false */) {
coding_t& coding = encode ? default_encoding : default_decoding;
switch (coding.type) {
case CODING_BY_FUNCTION:
{
Module* func_mod = coding.function_def->get_my_scope()->get_scope_mod();
if (usage_mod != func_mod) {
// add a phantom import for the coder function's module, to make sure
// it is visible from the module that requested the coding
Ttcn::Module* usage_mod_ttcn = dynamic_cast<Ttcn::Module*>(usage_mod);
if (usage_mod_ttcn == NULL) {
FATAL_ERROR("Type::chk_coding");
}
Ttcn::ImpMod* new_imp = new Ttcn::ImpMod(func_mod->get_modid().clone());
new_imp->set_mod(func_mod);
new_imp->set_imptype(Ttcn::ImpMod::I_DEPENDENCY);
usage_mod_ttcn->get_imports().add_impmod(new_imp);
}
}
// fall through
case CODING_BUILT_IN:
if (!delayed) {
// a coding method has been set by an external function's checker,
// but there might still be unchecked external functions out there;
// delay this function until everything else has been checked
Modules::delay_type_encode_check(this, encode);
Modules::delay_type_encode_check(this, usage_mod, encode);
}
return;
case CODING_UNSET:
......@@ -4550,7 +4566,7 @@ namespace Common {
// the type has a custom encoding attribute, but no external coder
// function with that encoding has been found yet;
// delay this function until everything else has been checked
Modules::delay_type_encode_check(this, encode);
Modules::delay_type_encode_check(this, usage_mod, encode);
// set the coding type back to UNSET for delayed call
coding.type = CODING_UNSET;
}
......@@ -4562,7 +4578,7 @@ namespace Common {
else { // ASN.1 type
// delay this function until everything else has been checked, in case
// there is an encoder/decoder external function out there for this type
Modules::delay_type_encode_check(this, encode);
Modules::delay_type_encode_check(this, usage_mod, encode);
}
}
......
......@@ -692,7 +692,7 @@ namespace Common {
* @note Because this check depends on the checks of other AST elements
* (external functions), it is sometimes delayed to the end of the semantic
* analysis. */
void chk_coding(bool encode, bool delayed = false);
void chk_coding(bool encode, Module* usage_mod, bool delayed = false);
/** Indicates whether the type is encoded/decoded by a function or by a
* built-in codec. */
......
......@@ -5782,7 +5782,8 @@ bool Type::chk_this_template_Str(Template *t, namedbool implicit_omit,
target->get_Template(), (target->get_DerivedRef() != NULL) ?
INCOMPLETE_ALLOWED : INCOMPLETE_NOT_ALLOWED,
OMIT_NOT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK, implicit_omit, lhs);
target_type->get_type_refd_last()->chk_coding(false);
target_type->get_type_refd_last()->chk_coding(false,
t->get_my_scope()->get_scope_mod());
}
{
Value* str_enc = t->get_string_encoding();
......
......@@ -5658,7 +5658,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
}*/
if(!disable_attribute_validation()) {
t_type->chk_coding(true);
t_type->chk_coding(true, my_scope->get_scope_mod());
}
switch (t_type->get_typetype()) {
......@@ -5810,7 +5810,7 @@ error:
}
if(!disable_attribute_validation()) {
t_type->chk_coding(false);
t_type->chk_coding(false, my_scope->get_scope_mod());
}
return;
......
......@@ -1288,6 +1288,10 @@ namespace Ttcn {
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)) {
......@@ -1399,6 +1403,8 @@ namespace Ttcn {
break;
}
case I_DEPENDENCY:
return false;
default:
FATAL_ERROR("ImpMod::get_imported_def");
}
......@@ -1489,6 +1495,8 @@ namespace Ttcn {
return result;
break;
}
case I_DEPENDENCY:
return NULL;
default:
FATAL_ERROR("ImpMod::get_imported_def");
}
......@@ -1579,6 +1587,16 @@ namespace Ttcn {
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);
}
......
......@@ -646,7 +646,7 @@ namespace Ttcn {
void set_with_attr(MultiWithAttrib* p_attrib);
WithAttribPath* get_attrib_path();
void set_parent_path(WithAttribPath* p_path);
const Imports& get_imports() const { return *imp; }
Imports& get_imports() { return *imp; }
bool is_visible(const Identifier& id, visibility_t visibility);
......@@ -719,7 +719,14 @@ namespace Ttcn {
I_ERROR,
I_ALL,
I_IMPORTSPEC,
I_IMPORTIMPORT
I_IMPORTIMPORT,
/** Phantom import for when the 'include' is needed in the generated C++
* code, but the TTCN-3 'import' statement is optional.
* Currently this can only happen when using coding values of a type that
* has custom or PER coder functions set (the module containing the coder
* function declarations does not have to be imported to the module that
* contains the coding operation). */
I_DEPENDENCY
};
private:
/** Points to the target (imported) module. This is initially NULL;
......
......@@ -8939,7 +8939,8 @@ error:
if (!error_flag && t_var_type != NULL) {
// make sure the variable's type has a decoding type set, and store it
t_parass->set_dec_type(t_var_type->get_type_refd());
t_parass->get_dec_type()->chk_coding(false);
t_parass->get_dec_type()->chk_coding(false,
t_parass->get_ref()->get_my_scope()->get_scope_mod());
}
}
else {
......@@ -9745,7 +9746,8 @@ error:
// store the variable type in case it's decoded (since this cannot
// be extracted from the value type with the sub-references)
v[i]->set_dec_type(var_type->get_type_refd());
v[i]->get_dec_type()->chk_coding(false);
v[i]->get_dec_type()->chk_coding(false,
v[i]->get_var_ref()->get_my_scope()->get_scope_mod());
}
}
else {
......
......@@ -161,3 +161,50 @@ INTEGER f__dec__choice(BITSTRING& x, Types::Choice& y)
}
} // namespace Custom1
namespace Custom5 {
BITSTRING f__enc__set(const Custom3::Set& x)
{
return int2bit(x.num(), 8);
}
INTEGER f__dec__set(BITSTRING& b, Custom3::Set& x)
{
x.num() = bit2int(b);
x.str() = "c++";
b = BITSTRING(0, NULL);
return 0;
}
BITSTRING f__enc__setof(const Types::SetOf& x)
{
TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_DEFAULT);
TTCN_Buffer buf;
x.encode(Types::SetOf_descr_, buf, TTCN_EncDec::CT_JSON, false);
OCTETSTRING tmp;
buf.get_string(tmp);
return oct2bit(tmp);
}
INTEGER f__dec__setof(BITSTRING& x, Types::SetOf& y)
{
TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
TTCN_Buffer buf(bit2oct(x));
y.decode(Types::SetOf_descr_, buf, TTCN_EncDec::CT_JSON);
switch (TTCN_EncDec::get_last_error_type()) {
case TTCN_EncDec::ET_NONE: {
buf.cut();
OCTETSTRING tmp;
buf.get_string(tmp);
x = oct2bit(tmp);
return 0; }
case TTCN_EncDec::ET_INCOMPL_MSG:
case TTCN_EncDec::ET_LEN_ERR:
return 2;
default:
return 1;
}
}
} // namespace Custom5
......@@ -13,6 +13,7 @@
#include "Custom2.hh"
#include "Custom1.hh"
#include "Custom5.hh"
#ifndef CODERS_HH
#define CODERS_HH
......@@ -27,6 +28,7 @@ INTEGER f__dec__rec(BITSTRING& b, Custom3::Rec& x);
BITSTRING f__enc__uni(const Custom1::Uni& x);
INTEGER f__dec__uni(BITSTRING& b, Custom1::Uni& x);
// Coding functions for the bitstring type in test 4
BITSTRING f__enc__bs(const BITSTRING& x);
INTEGER f__dec__bs(BITSTRING& b, BITSTRING& x);
......@@ -45,4 +47,16 @@ extern INTEGER f__dec__choice(BITSTRING& x, Types::Choice& y);
}
namespace Custom5 {
// Coding functions for the set type in test 5
BITSTRING f__enc__set(const Custom3::Set& x);
INTEGER f__dec__set(BITSTRING& b, Custom3::Set& x);
// "PER" coder functions, using the built-in JSON codec
extern BITSTRING f__enc__setof(const Types::SetOf& x);
extern INTEGER f__dec__setof(BITSTRING& x, Types::SetOf& y);
}
#endif
......@@ -165,6 +165,30 @@ testcase tc_custom4() runs on CT
}
// Test 5.
// The encoded type is a set defined in another module
// The coding functions are declared in a 3rd module (which is not imported in this module)
testcase tc_custom5() runs on CT
{
var Set x := { num := 3, str := "ttcn" };
var bitstring enc_exp := int2bit(x.num, 8);
var Set dec_exp := { num := 3, str := "c++" };
var bitstring enc := encvalue(x);
if (enc != enc_exp) {
setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
}
var Set dec;
var integer res := decvalue(enc_exp, dec);
if (res != 0) {
setverdict(fail, "Failed to decode ", enc_exp);
}
if (dec != dec_exp) {
setverdict(fail, "Expected: ", dec_exp, ", got: ", dec);
}
setverdict(pass);
}
// Test 6.
// Using encvalue on templates and template variables
// Same type and encoding function as test 1
testcase tc_custom_temp() runs on CT
......@@ -185,7 +209,7 @@ testcase tc_custom_temp() runs on CT
setverdict(pass);
}
// Test 6.
// Test 7.
// Same as test 2, but with encvalue_unichar and decvalue_unichar.
// The input RecOf has also been adjusted, so it doesn't cause problems for the UTF-8 decoder.
testcase tc_custom_unichar() runs on CT
......@@ -209,7 +233,7 @@ testcase tc_custom_unichar() runs on CT
setverdict(pass);
}
// Test 7.
// Test 8.
// Using custom encoding on a decoded parameter redirect.
// Same input value as in test 5.
testcase tc_custom_param_redirect() runs on CT
......@@ -240,7 +264,7 @@ testcase tc_custom_param_redirect() runs on CT
}
}
// Test 8.
// Test 9.
// Using custom encoding in a template with decoded content matching.
// Same input as test 3.
testcase tc_custom_decmatch() runs on CT
......@@ -272,7 +296,7 @@ external function f_enc_seq(in Seq x) return octetstring
external function f_dec_seq(in octetstring x) return Seq
with { extension "prototype(convert) decode(JSON)" };
// Test 9.
// Test 10.
// Using encvalue and decvalue on the ASN.1 type Seq (with JSON encoding).
testcase tc_asn() runs on CT
{
......@@ -295,7 +319,31 @@ testcase tc_asn() runs on CT
setverdict(pass);
}
// Test 10.
// Test 11.
// Using encvalue and decvalue on the ASN.1 type Set (with JSON encoding).
// The external functions indicating the type's coding are in a different module, which is not imported.
testcase tc_asn2() runs on CT
{
var Set2 x := { num := 10, str := "abc" };
var bitstring enc_exp := oct2bit(char2oct("{\"num\":10,\"str\":\"abc\"}"));
var Set2 dec_exp := x;
var bitstring enc := encvalue(x);
if (enc != enc_exp) {
setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
}
var Set2 dec;
var integer res := decvalue(enc_exp, dec);
if (res != 0) {
setverdict(fail, "Failed to decode ", enc_exp);
}
if (dec != dec_exp) {
setverdict(fail, "Expected: ", dec_exp, ", got: ", dec);
}
setverdict(pass);
}
// Test 12.
// The redirected parameter is decoded into a value of ASN.1 type Seq.
// Same input value as in test 8.
testcase tc_asn_param_redirect() runs on CT
......@@ -326,7 +374,7 @@ testcase tc_asn_param_redirect() runs on CT
}
}
// Test 11.
// Test 13.
// Decoded content matching against a value of ASN.1 type Seq.
// Same input value as in test 8.
testcase tc_asn_decmatch() runs on CT
......@@ -359,7 +407,7 @@ external function f_enc_seqof(in SeqOf x) return bitstring
external function f_dec_seqof(inout bitstring x, out SeqOf y) return integer
with { extension "prototype(sliding) decode(PER)" };
// Test 12.
// Test 14.
// PER coder with encvalue and decvalue.
testcase tc_per() runs on CT
{
......@@ -383,7 +431,32 @@ testcase tc_per() runs on CT
setverdict(pass);
}
// Test 13.
// Test 15.
// PER coder with encvalue and decvalue.
// The PER coder functions are declared in a module, which is not imported into this one.
testcase tc_per2() runs on CT
{
var SetOf x := { 1, 2, 3 };
// the manually written "PER" coder for this module uses the built-in JSON coder
var bitstring enc_exp := oct2bit(char2oct("[1,2,3]"));
var SetOf dec_exp := x;
var bitstring enc := encvalue(x);
if (enc != enc_exp) {
setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
}
var SetOf dec;
var integer res := decvalue(enc_exp, dec);
if (res != 0) {
setverdict(fail, "Failed to decode ", enc_exp);
}
if (dec != dec_exp) {
setverdict(fail, "Expected: ", dec_exp, ", got: ", dec);
}
setverdict(pass);
}
// Test 16.
// The redirected parameter is decoded using the PER coder.
// Same input value as in test 11.
testcase tc_per_param_redirect() runs on CT
......@@ -414,7 +487,7 @@ testcase tc_per_param_redirect() runs on CT
}
}
// Test 14.
// Test 17.
// Decoded content matching with the PER coder.
// Same input value as in test 11.
testcase tc_per_decmatch() runs on CT
......@@ -447,7 +520,7 @@ external function f_enc_choice(in Choice x) return bitstring
external function f_dec_choice(inout bitstring x, out Choice y) return integer
with { extension "prototype(sliding) decode(PER)" };
// Test 15.
// Test 18.
// Encoding an ASN.1 type with JSON (built-in) and decoding the same type with PER (manual).
testcase tc_asn_mixed() runs on CT
{
......@@ -478,14 +551,17 @@ control {
execute(tc_custom2());
execute(tc_custom3());
execute(tc_custom4());
execute(tc_custom5());
execute(tc_custom_temp());
execute(tc_custom_unichar());
execute(tc_custom_param_redirect());
execute(tc_custom_decmatch());
execute(tc_asn());
execute(tc_asn2());
execute(tc_asn_param_redirect());
execute(tc_asn_decmatch());
execute(tc_per());
execute(tc_per2());
execute(tc_per_param_redirect());
execute(tc_per_decmatch());
execute(tc_asn_mixed());
......
......@@ -22,4 +22,13 @@ with {
encode "localCustom";
}
// Set type for test 5
type set Set {
integer num,
charstring str
}
with {
encode "localCustom";
}
}
......@@ -19,7 +19,7 @@ import from Custom2 all;
import from Custom3 all;
import from Types all;
// Test 16 (RT2 only).
// Test 19 (RT2 only).
// Using custom encoding on a decoded value redirect.
// Same input value as in test 1.
testcase tc_custom_value_redirect() runs on CT
......@@ -50,7 +50,7 @@ testcase tc_custom_value_redirect() runs on CT
}
}
// Test 17 (RT2 only).
// Test 20 (RT2 only).
// The redirected value is decoded into a value of ASN.1 type Seq.
// Same input value as in test 8.
testcase tc_asn_value_redirect() runs on CT
......@@ -80,7 +80,7 @@ testcase tc_asn_value_redirect() runs on CT
}
}
// Test 18 (RT2 only).
// Test 21 (RT2 only).
// The redirected value is decoded using the PER coder.
// Same input value as in test 11.
testcase tc_per_value_redirect() runs on CT
......
/******************************************************************************
* Copyright (c) 2000-2016 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Baranyi, Botond
*
******************************************************************************/
module Custom5 {
import from Custom3 all;
import from Types all;
// Coding function declarations for test 5
external function f_enc_set(in Set x) return bitstring
with { extension "prototype(convert) encode(localCustom)" }
external function f_dec_set(inout bitstring b, out Set x) return integer
with { extension "prototype(sliding) decode(localCustom)" }
// these let encvalue and decvalue know which encoding to use for the ASN.1 type
external function f_enc_set2(in Set2 x) return octetstring
with { extension "prototype(convert) encode(JSON)" };
external function f_dec_set2(in octetstring x) return Set2
with { extension "prototype(convert) decode(JSON)" };
// PER coder functions, these need to be implemented manually, just like custom
// coder functions
external function f_enc_setof(in SetOf x) return bitstring
with { extension "prototype(convert) encode(PER)" };
external function f_dec_setof(inout bitstring x, out SetOf y) return integer
with { extension "prototype(sliding) decode(PER)" };
}
......@@ -19,7 +19,7 @@ include $(TOPDIR)/Makefile.regression
TTCN3_LIB = ttcn3$(RT2_SUFFIX)$(DYNAMIC_SUFFIX)
TTCN3_MODULES = Custom1.ttcn Custom2.ttcn Custom3.ttcn
TTCN3_MODULES = Custom1.ttcn Custom2.ttcn Custom3.ttcn Custom5.ttcn
ASN1_MODULES = Types.asn
ifdef RT2
......
......@@ -23,8 +23,15 @@ Seq ::= SEQUENCE {
str VisibleString
}
Set2 ::= SET {
num INTEGER,
str VisibleString
}
SeqOf ::= SEQUENCE OF INTEGER
SetOf ::= SEQUENCE OF INTEGER
Choice ::= CHOICE {