From 78a82971bdd606e92e522b9808e27991ddad7dc9 Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Wed, 28 Mar 2018 16:48:17 +0200
Subject: [PATCH] Implemented operation 'port.getref()' (bug 533006)

Change-Id: I9158d29e0e81ed2265a148081d8bcea6f3e82025
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 compiler2/Value.cc                            | 54 +++++++++++++++++++
 compiler2/Value.hh                            |  5 +-
 compiler2/ttcn3/AST_ttcn3.cc                  | 21 ++++++--
 compiler2/ttcn3/Ttcnstuff.cc                  |  6 ++-
 compiler2/ttcn3/Ttcnstuff.hh                  |  1 +
 compiler2/ttcn3/compiler.l                    |  2 +
 compiler2/ttcn3/compiler.y                    | 17 ++++--
 core/Runtime.cc                               |  7 +++
 core/Runtime.hh                               |  1 +
 .../portTranslation/PortTranslation.ttcn      | 19 +++++--
 xsdconvert/converter.cc                       |  2 +-
 11 files changed, 121 insertions(+), 14 deletions(-)

diff --git a/compiler2/Value.cc b/compiler2/Value.cc
index c4a679e1e..d551c1c13 100644
--- a/compiler2/Value.cc
+++ b/compiler2/Value.cc
@@ -171,6 +171,9 @@ namespace Common {
       case OPTYPE_TESTCASENAME:
       case OPTYPE_PROF_RUNNING:
         break;
+      case OPTYPE_GET_PORT_REF:
+        u.expr.type = p.u.expr.type; // the type is not owned, don't copy it
+        break;
       case OPTYPE_COMP_RUNNING: // v1 [r2] b4
       case OPTYPE_COMP_ALIVE:
         u.expr.r2 = p.u.expr.r2 != NULL ? p.u.expr.r2->clone() : NULL;
@@ -526,6 +529,7 @@ namespace Common {
     case OPTYPE_GETVERDICT:
     case OPTYPE_TESTCASENAME:
     case OPTYPE_PROF_RUNNING:
+    case OPTYPE_GET_PORT_REF: // type (not owned)
       break;
     case OPTYPE_COMP_RUNNING: // v1 [r2] b4
     case OPTYPE_COMP_ALIVE:
@@ -928,6 +932,9 @@ namespace Common {
     case OPTYPE_TESTCASENAME:
     case OPTYPE_PROF_RUNNING:
       break;
+    case OPTYPE_GET_PORT_REF:
+      u.expr.type = NULL; // will be set during semantic analysis
+      break;
     default:
       FATAL_ERROR("Value::Value()");
     } // switch
@@ -1751,6 +1758,7 @@ namespace Common {
     case OPTYPE_GETVERDICT:
     case OPTYPE_TESTCASENAME:
     case OPTYPE_PROF_RUNNING:
+    case OPTYPE_GET_PORT_REF:
       break;
     case OPTYPE_UNARYPLUS: // v1
     case OPTYPE_UNARYMINUS:
@@ -1998,6 +2006,7 @@ namespace Common {
     case OPTYPE_GETVERDICT:
     case OPTYPE_TESTCASENAME:
     case OPTYPE_PROF_RUNNING:
+    case OPTYPE_GET_PORT_REF:
       break;
     case OPTYPE_COMP_RUNNING: // v1 [r2] b4
     case OPTYPE_COMP_ALIVE:
@@ -2373,6 +2382,7 @@ namespace Common {
       case OPTYPE_GETVERDICT:
       case OPTYPE_TESTCASENAME:
       case OPTYPE_PROF_RUNNING:
+      case OPTYPE_GET_PORT_REF:
         break;
       case OPTYPE_COMP_RUNNING: // v1 [r2] b4
       case OPTYPE_COMP_ALIVE:
@@ -3345,6 +3355,8 @@ namespace Common {
         return Type::T_BOOL;
       case OPTYPE_GETVERDICT:
         return Type::T_VERDICT;
+      case OPTYPE_GET_PORT_REF:
+        return Type::T_PORT;
       case OPTYPE_VALUEOF: {
         Error_Context cntxt(this, "In the operand of operation `%s'",
                             get_opname());
@@ -3790,6 +3802,9 @@ namespace Common {
 	} else return 0;
       case OPTYPE_COMP_CREATE:
 	return chk_expr_operand_comptyperef_create();
+      case OPTYPE_GET_PORT_REF:
+        chk_expr_operands(NULL, exp_val); // calculate the port type
+        return u.expr.type;
       default:
         break;
       }
@@ -4079,6 +4094,8 @@ namespace Common {
       return "ttcn2string()";
     case OPTYPE_PROF_RUNNING:
       return "@profiler.running";
+    case OPTYPE_GET_PORT_REF:
+      return "port.getref()";
     default:
       FATAL_ERROR("Value::get_opname()");
     } // switch
@@ -6876,6 +6893,26 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
     case OPTYPE_TESTCASENAME:
     case OPTYPE_PROF_RUNNING:
       break;
+    case OPTYPE_GET_PORT_REF:
+      if (u.expr.type == NULL) {
+        Ttcn::PortScope* port_scope = my_scope->get_scope_port();
+        if (port_scope == NULL) {
+          error("Operation `%s' can only be used in a function with a port clause.",
+            opname);
+          set_valuetype(V_ERROR);
+          break;
+        }
+        u.expr.type = port_scope->get_port_type();
+        if (u.expr.type == NULL) {
+          FATAL_ERROR("Value::chk_expr_operands");
+        }
+        if (my_governor != NULL && !u.expr.type->is_identical(my_governor)) {
+          error("Type mismatch: A value of type `%s' was expected instead of `%s'",
+            my_governor->get_typename().c_str(), u.expr.type->get_typename().c_str());
+          set_valuetype(V_ERROR);
+        }
+      }
+      break;
     case OPTYPE_COMP_MTC:
     case OPTYPE_COMP_SYSTEM:
       chk_expr_comptype_compat();
@@ -7951,6 +7988,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
     case OPTYPE_TMR_RUNNING_ANY:
     case OPTYPE_GETVERDICT:
     case OPTYPE_PROF_RUNNING:
+    case OPTYPE_GET_PORT_REF:
     case OPTYPE_RNDWITHVAL: // v1
     case OPTYPE_COMP_RUNNING: // v1
     case OPTYPE_COMP_ALIVE:
@@ -9275,6 +9313,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
       case OPTYPE_GETVERDICT:
       case OPTYPE_TESTCASENAME:
       case OPTYPE_PROF_RUNNING:
+      case OPTYPE_GET_PORT_REF:
       case OPTYPE_RNDWITHVAL: // v1
       case OPTYPE_MATCH: // v1 t2
       case OPTYPE_UNDEF_RUNNING: // v1
@@ -10949,6 +10988,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
     case OPTYPE_TMR_RUNNING_ANY: // -
     case OPTYPE_GETVERDICT: // -
     case OPTYPE_PROF_RUNNING: // -
+    case OPTYPE_GET_PORT_REF: // -
     case OPTYPE_CHECKSTATE_ANY:
     case OPTYPE_CHECKSTATE_ALL:
       break; // nothing to do
@@ -11825,6 +11865,8 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
         return ret_val; }
       case OPTYPE_PROF_RUNNING:
         return string("@profiler.running");
+      case OPTYPE_GET_PORT_REF:
+        return string("port.getref()");
       default:
         return string("<unsupported optype>");
       } // switch u.expr.v_optype
@@ -13419,6 +13461,17 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
     case OPTYPE_PROF_RUNNING:
       expr->expr = mputstr(expr->expr, "ttcn3_prof.is_running()");
       break;
+    case OPTYPE_GET_PORT_REF: {
+      string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id();
+      expr->preamble = mputprintf(expr->preamble,
+        "%s* %s = dynamic_cast<%s*>(TTCN_Runtime::get_translation_port());\n"
+        "if (%s == NULL) TTCN_error(\"Internal error: Conversion of port "
+        "reference to type `%s' failed.\");\n",
+        u.expr.type->get_genname_value(my_scope).c_str(), tmp_id.c_str(),
+        u.expr.type->get_genname_value(my_scope).c_str(), tmp_id.c_str(),
+        u.expr.type->get_typename().c_str());
+      expr->expr = mputprintf(expr->expr, "(*%s)", tmp_id.c_str());
+      break; }
     default:
       FATAL_ERROR("Value::generate_code_expr_expr()");
     }
@@ -15099,6 +15152,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
     case OPTYPE_TTCN2STRING:
     case OPTYPE_ENCVALUE_UNICHAR:
     case OPTYPE_DECVALUE_UNICHAR:
+    case OPTYPE_GET_PORT_REF:
       return false;
     case OPTYPE_COMP_RUNNING: // v1 [r2] b4
     case OPTYPE_COMP_ALIVE:
diff --git a/compiler2/Value.hh b/compiler2/Value.hh
index 3c5761cc6..46097c121 100644
--- a/compiler2/Value.hh
+++ b/compiler2/Value.hh
@@ -278,6 +278,8 @@ namespace Common {
       OPTYPE_JSON2CBOR, // v1
       OPTYPE_BSON2JSON, // v1
       OPTYPE_JSON2BSON, // v1
+      
+      OPTYPE_GET_PORT_REF, // -
 
       NUMBER_OF_OPTYPES // must be last
     };
@@ -343,6 +345,7 @@ namespace Common {
           TemplateInstance *ti1;
           Ttcn::Ref_base *r1; /**< timer or component */
           LogArguments *logargs; /**< arguments of log2str() */
+          Type* type;
         };
         union {
           Value *v2;
@@ -413,7 +416,7 @@ namespace Common {
     /** Constructor used by V_EXPR "-": RND, TESTCASENAME, COMP_NULL, COMP_MTC,
      *  COMP_SYSTEM, COMP_SELF, COMP_RUNNING_ANY, COMP_RUNNING_ALL,
      *  COMP_ALIVE_ALL, COMP_ALIVE_ANY, TMR_RUNNING_ANY, GETVERDICT,
-     *  PROF_RUNNING */
+     *  PROF_RUNNING, OPTYPE_GET_PORT_REF */
     Value(operationtype_t p_optype);
     /** Constructor used by V_EXPR "v1" or [v1] */
     Value(operationtype_t p_optype, Value *p_v1);
diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index acbbab69a..a804f74c6 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -8382,11 +8382,10 @@ namespace Ttcn {
     if (!type) FATAL_ERROR("FormalPar::get_Type()");
     return type;
   }
-
+  
   void FormalPar::chk()
   {
     if (checked) return;
-    checked = true;
     TemplateInstance *default_value = defval.ti;
     defval.ti = 0;
     if (type) {
@@ -8400,6 +8399,8 @@ namespace Ttcn {
         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());
@@ -8428,7 +8429,9 @@ namespace Ttcn {
         }
       }
     } else if (asstype != A_PAR_TIMER) FATAL_ERROR("FormalPar::chk()");
-
+    
+    checked = true;
+    
     if (default_value) {
       Error_Context cntxt(default_value, "In default value");
       defval.ap = chk_actual_par(default_value, Type::EXPECTED_STATIC_VALUE);
@@ -9016,6 +9019,18 @@ namespace Ttcn {
       }
       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();
diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc
index dfee3c3cc..6ef9b2598 100644
--- a/compiler2/ttcn3/Ttcnstuff.cc
+++ b/compiler2/ttcn3/Ttcnstuff.cc
@@ -1038,7 +1038,7 @@ namespace Ttcn {
     : Node(), Location(), my_type(0), operation_mode(p_operation_mode),
     in_list(p_in_list), out_list(p_out_list), inout_list(p_inout_list),
     in_all(p_in_all), out_all(p_out_all), inout_all(p_inout_all),
-    checked(false), legacy(true),
+    checked(false), attributes_checked(false), legacy(true),
     in_msgs(0), out_msgs(0), in_sigs(0), out_sigs(0),
     testport_type(TP_REGULAR), port_type(PT_REGULAR),
     provider_refs(), provider_types(), mapper_types(),
@@ -1784,6 +1784,10 @@ namespace Ttcn {
   {
     if (!w_attrib_path || !checked || !my_type)
       FATAL_ERROR("PortTypeBody::chk_attributes()");
+    if (attributes_checked) {
+      return;
+    }
+    attributes_checked = true;
 
     Ttcn::ExtensionAttributes * extarts = parse_extattributes(w_attrib_path);
     if (extarts != 0) { // NULL means parsing error
diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh
index 6d937e98c..95a36a730 100644
--- a/compiler2/ttcn3/Ttcnstuff.hh
+++ b/compiler2/ttcn3/Ttcnstuff.hh
@@ -390,6 +390,7 @@ private:
   Types *in_list, *out_list, *inout_list;
   bool in_all, out_all, inout_all; // whether "(in|out|inout) all" was used
   bool checked;
+  bool attributes_checked;
   bool legacy; // Old extension syntax or new standard syntax
   /* Types and signatures that can be sent and received.
    * These are initially empty; filled by PortTypeBody::chk_list based on
diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l
index 603560351..22b41f9c6 100644
--- a/compiler2/ttcn3/compiler.l
+++ b/compiler2/ttcn3/compiler.l
@@ -535,6 +535,8 @@ xor4b		RETURN(Xor4bKeyword);
 "@profiler" RETURN(TitanSpecificProfilerKeyword);
 "@update" RETURN(TitanSpecificUpdateKeyword);
 
+"getref"  RETURN_DOT(GetRefKeyword);
+
 	/* Predefined function identifiers */
 
 bit2hex		RETURN(bit2hexKeyword);
diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y
index f396e1040..f122f657c 100644
--- a/compiler2/ttcn3/compiler.y
+++ b/compiler2/ttcn3/compiler.y
@@ -684,6 +684,7 @@ static const string anyname("anytype");
 %token FromKeyword
 %token FunctionKeyword
 %token GetCallOpKeyword
+%token GetRefKeyword
 %token GetReplyOpKeyword
 %token GetVerdictKeyword
 %token GotoKeyword
@@ -819,6 +820,7 @@ static const string anyname("anytype");
 %token DotCreateKeyword
 %token DotDoneKeyword
 %token DotGetCallOpKeyword
+%token DotGetRefKeyword
 %token DotGetReplyOpKeyword
 %token DotHaltKeyword
 %token DotKillKeyword
@@ -1899,16 +1901,16 @@ optDecodedModifier
 %left '*' '/' ModKeyword RemKeyword
 %left UnarySign
 
-%expect 67
+%expect 69
 
 %start GrammarRoot
 
 /*
-XXX Source of conflicts (66 S/R):
+XXX Source of conflicts (69 S/R):
 
-1.) 9 conflicts in one state
+1.) 10 conflicts in one state
 The Expression after 'return' keyword is optional in ReturnStatement.
-For 9 tokens the parser cannot decide whether the token is a part of
+For 10 tokens the parser cannot decide whether the token is a part of
 the return expression (shift) or it is the beginning of the next statement
 (reduce).
 
@@ -1947,7 +1949,7 @@ non-standard language extension.
 
 7.) 27 conflicts in one state
 In the DecodedContentMatch rule a SingleExpression encased in round brackets is
-followed by an in-line template. For 26 tokens (after the ')' ) the parser cannot
+followed by an in-line template. For 27 tokens (after the ')' ) the parser cannot
 decide whether the token is the beginning of the in-line template (shift) or
 the brackets are only part of the SingleExpression itself and the conflicting
 token is the next segment in the expression (reduce).
@@ -9437,6 +9439,11 @@ OpCall: // 611
     $$ = new Value(Value::OPTYPE_CHECKSTATE_ALL, r, $5);
     $$->set_location(infile, @$);
   }
+| PortKeyword DotGetRefKeyword '(' ')'
+  {
+    $$ = new Value(Value::OPTYPE_GET_PORT_REF);
+    $$->set_location(infile, @$);
+  }
 ;
 
 PredefinedOps:
diff --git a/core/Runtime.cc b/core/Runtime.cc
index d059d9696..844ed1308 100644
--- a/core/Runtime.cc
+++ b/core/Runtime.cc
@@ -141,6 +141,13 @@ void TTCN_Runtime::set_port_state(const INTEGER& state, const CHARSTRING& info,
   }
 }
 
+PORT* TTCN_Runtime::get_translation_port() {
+  if (p == NULL) {
+    TTCN_error("Operation 'port.getref' was called while not in a port translation procedure.");
+  }
+  return p;
+}
+
 void TTCN_Runtime::set_translation_mode(boolean enabled, PORT* port) {
   if (enabled) {
     translation_count++;
diff --git a/core/Runtime.hh b/core/Runtime.hh
index 2a90e372c..a665f575d 100644
--- a/core/Runtime.hh
+++ b/core/Runtime.hh
@@ -135,6 +135,7 @@ public:
   
   static void set_port_state(const INTEGER& state, const CHARSTRING& info, boolean by_system);
   static void set_translation_mode(boolean enabled, PORT* port);
+  static PORT* get_translation_port();
 
 private:
   inline static boolean in_controlpart()
diff --git a/regression_test/portTranslation/PortTranslation.ttcn b/regression_test/portTranslation/PortTranslation.ttcn
index 7cf7c3e39..354dc2d93 100644
--- a/regression_test/portTranslation/PortTranslation.ttcn
+++ b/regression_test/portTranslation/PortTranslation.ttcn
@@ -208,8 +208,19 @@ module PortTranslation {
 		extension "prototype(fast)";
 	}
 	
-	function char_to_hex3(in charstring i, out hexstring j) {
+	function f_port_ref_test(PT3 p1, inout PT3 p2)
+	{
+	  if (log2str(p1) != "port p3") {
+	    setverdict(fail, "First port argument is invalid. Expected port p3, got: ", p1);
+	  }
+	  if (log2str(p2) != "port p3") {
+	    setverdict(fail, "First port argument is invalid. Expected port p3, got: ", p1);
+	  }
+	}
+	
+	function char_to_hex3(in charstring i, out hexstring j) port PT3 {
 	  j := oct2hex(char2oct(i));
+	  f_port_ref_test(port.getref(), port.getref());
 	  port.setstate(0); // translated
 	} with {
 		extension "prototype(fast)";
@@ -788,7 +799,8 @@ module PortTranslation {
 	}
 	
 	// This tests the 'discard' option in the 'setstate' operation when receiving
-	testcase tc_receive_discarded() runs on MyComp system System {
+	// It also tests the operation 'port.getref()'
+	testcase tc_receive_discarded_plus_port_ref() runs on MyComp system System {
 	  map(self:p3, system:p4);
 	  
 	  p3.send(123);
@@ -810,6 +822,7 @@ module PortTranslation {
 		// an integer (-10) and a charstring ("abc") are received on the provider port,
 		// the translation functions discard the integer and convert the charstring into a hexstring,
 		// so only the hexstring is received on the main port
+		// (the charstring-to-hexstring translation function also contains tests for operation 'port.getref()')
 		alt {
 		  [] p3.receive(hexstring: '616263'H) { setverdict(pass); }
 		  [] p3.receive(hexstring: ?) -> value bad { setverdict(fail, "Test #2 failed. Received invalid hexstring: ", bad); }
@@ -836,7 +849,7 @@ module PortTranslation {
 		execute(tc_receive_partially_translated());
 		execute(tc_receive_fragmented());
 		
-		execute(tc_receive_discarded());
+		execute(tc_receive_discarded_plus_port_ref());
 	}
 
 }
diff --git a/xsdconvert/converter.cc b/xsdconvert/converter.cc
index 9494ad5bb..ab1b3a8e4 100644
--- a/xsdconvert/converter.cc
+++ b/xsdconvert/converter.cc
@@ -253,7 +253,7 @@ static void printUsage(const char * argv0) {
     "	-f|J file:	the names of XSD files are taken from file instead of the command line\n"
     "	-g:		generate TTCN-3 code disallowing element substitution\n"
     "	-h:		generate TTCN-3 code allowing type substitution\n"
-    "	-m:		generate only the UsefulTtcn3Types and XSD predefined modules"
+    "	-m:		generate only the UsefulTtcn3Types and XSD predefined modules\n"
     "	-p:		do not generate the UsefulTtcn3Types and XSD predefined modules\n"
     "	-q:		quiet mode - disable the issue of status messages\n"
     "	-s:		parse and validate only - no TTCN-3 module generation\n"
-- 
GitLab