From 755065ad38df0daca7bad2d4ce88adb1684fc025 Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Thu, 29 Apr 2021 17:27:32 +0200
Subject: [PATCH] OOP: catching exceptions of class type is now determined by
 the dynamic class type, instead of the static type raised (issue #533)

Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 compiler2/ttcn3/AST_ttcn3.cc        |  15 ++++-
 compiler2/ttcn3/Statement.cc        |  53 +++++++--------
 core/OOP.hh                         | 101 +++++++++++++++++++---------
 regression_test/oop/exceptions.ttcn |   8 ++-
 4 files changed, 113 insertions(+), 64 deletions(-)

diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index 647b8cb2a..346949a69 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -5578,9 +5578,18 @@ namespace Ttcn {
   
   char* Def_Exception::generate_code_str(char *str)
   {
-    return usage_found ? mputprintf(str, "EXCEPTION< %s >& %s = static_cast<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()) :
-      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());
   }
 
   // =================================
diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc
index 5622e8b44..dec16d6ce 100644
--- a/compiler2/ttcn3/Statement.cc
+++ b/compiler2/ttcn3/Statement.cc
@@ -594,7 +594,7 @@ namespace Ttcn {
       // C++11 version:
       string tmp_id = get_scope_mod_gen()->get_temporary_id();
       str = mputprintf(str,
-        "FINALLY %s([&] {\n", tmp_id.c_str());
+        "FINALLY_CPP11 %s([&] {\n", tmp_id.c_str());
       str = finally_block->generate_code(str, def_glob_vars, src_glob_vars);
       str = mputstr(str, "});\n");
 #endif
@@ -630,9 +630,18 @@ namespace Ttcn {
         "catch (EXCEPTION_BASE& exc_base) {\n");
       for (size_t i = 0; i < catch_blocks.size(); ++i) {
         Type* exc_type = catch_blocks[i]->get_stmt_byIndex(0)->get_def()->get_Type();
-        str = mputprintf(str,
-          "%sif (exc_base.has_type(\"%s\")) {\n",
-          i > 0 ? "else " : "", exc_type->get_exception_name().c_str());
+        Type* exc_type_last = exc_type->get_type_refd_last();
+        if (exc_type_last->get_typetype() == Common::Type::T_CLASS) {
+          ClassTypeBody* class_ = exc_type_last->get_class_type_body();
+          str = mputprintf(str,
+            "%sif (exc_base.is_class() && dynamic_cast<%s*>(exc_base.get_object()) != NULL) {\n",
+            i > 0 ? "else " : "", class_->is_built_in() ? "OBJECT" : exc_type_last->get_genname_own(this).c_str());
+        }
+        else {
+          str = mputprintf(str,
+            "%sif (exc_base.is_type(\"%s\")) {\n",
+            i > 0 ? "else " : "", exc_type->get_exception_name().c_str());
+        }
         str = catch_blocks[i]->generate_code(str, def_glob_vars, src_glob_vars, alt_guards);
         str = mputstr(str, "}\n");
       }
@@ -8742,32 +8751,18 @@ error:
       str = mputstr(str, expr.preamble);
     }
     Common::Type* exc_type = raise_op.v->get_expr_governor(Common::Type::EXPECTED_DYNAMIC_VALUE);
+    Common::Type* exc_type_last = exc_type->get_type_refd_last();
     string exc_type_str = exc_type->get_genname_value(my_sb);
-    string id_str = my_sb->get_scope_mod_gen()->get_temporary_id();
-    bool is_class = exc_type->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS;
-    // the EXCEPTION class stores all the superclasses of the class type, too, since those
-    // can also catch this type of exception
-    int nof_exc_types = 1;
-    Type* t;
-    if (is_class) {
-      t = exc_type;
-      while (t != NULL) {
-        ++nof_exc_types;
-        t = t->get_type_refd_last()->get_class_type_body()->get_base_type();
-      }
-    }
-    str = mputprintf(str, "const char** %s = new const char*[%i];\n",
-      id_str.c_str(), nof_exc_types);
-    t = exc_type;
-    for (int i = 0; i < nof_exc_types; ++i) {
-      if (i > 0) {
-        t = t->get_type_refd_last()->get_class_type_body()->get_base_type();
-      }
-      str = mputprintf(str,  "%s[%i] = \"%s\";\n",
-        id_str.c_str(), i, t != NULL ? t->get_exception_name().c_str() : "object");
-    }
-    str = mputprintf(str, "throw EXCEPTION< %s >(new %s(%s), %s, %i);\n",
-      exc_type_str.c_str(), exc_type_str.c_str(), expr.expr, id_str.c_str(), nof_exc_types);
+    if (exc_type_last->get_typetype() == Common::Type::T_CLASS) {
+      ClassTypeBody* class_ = exc_type_last->get_class_type_body();
+      str = mputprintf(str, "throw CLASS_EXCEPTION<%s>(new %s(%s));\n",
+        class_->is_built_in() ? "OBJECT" : exc_type_last->get_genname_own(my_sb).c_str(),
+        exc_type_str.c_str(), expr.expr);
+    }
+    else {
+      str = mputprintf(str, "throw NON_CLASS_EXCEPTION< %s >(new %s(%s), \"%s\");\n",
+        exc_type_str.c_str(), exc_type_str.c_str(), expr.expr, exc_type->get_exception_name().c_str());
+    }
     if (expr.postamble != NULL) {
       str = mputstr(str, expr.postamble);
     }
diff --git a/core/OOP.hh b/core/OOP.hh
index de12f7c8c..607d4098f 100644
--- a/core/OOP.hh
+++ b/core/OOP.hh
@@ -208,6 +208,18 @@ public:
     }
     return OBJECT_REF<T2>(new_ptr);
   }
+  
+  template<typename T2>
+  OBJECT_REF<T2>* cast_to_dyn() const {
+    if (ptr == NULL) {
+      TTCN_error("Internal error: Casting a null reference");
+    }
+    T2* new_ptr = dynamic_cast<T2*>(ptr);
+    if (new_ptr == NULL) {
+      TTCN_error("Internal error: Invalid casting to class type `%s'", T2::class_name());
+    }
+    return new OBJECT_REF<T2>(new_ptr);
+  }
 };
 
 template<typename T>
@@ -224,73 +236,102 @@ boolean operator!=(null_type, const OBJECT_REF<T>& right_val) { // inequality op
 // ---------
 
 class EXCEPTION_BASE {
-private:
-  const char** exc_types;
-  size_t nof_types;
 protected:
   CHARSTRING exception_log; // this is set by the EXCEPTION class
 public:
-  EXCEPTION_BASE(const char** p_exc_types, size_t p_nof_types)
-  : exc_types(p_exc_types), nof_types(p_nof_types) { }
-  EXCEPTION_BASE(const EXCEPTION_BASE& p)
-  : exc_types(new const char*[p.nof_types]), nof_types(p.nof_types), exception_log(p.exception_log) {
-    for (size_t i = 0; i < nof_types; ++i) {
-      exc_types[i] = p.exc_types[i];
-    }
+  EXCEPTION_BASE() { }
+  EXCEPTION_BASE(const EXCEPTION_BASE& p): exception_log(p.exception_log) { }
+  virtual ~EXCEPTION_BASE() { }
+  CHARSTRING get_log() const { return exception_log; }
+  virtual boolean is_class() const { return FALSE; }
+  virtual boolean is_type(const char* p_type_name) const { return FALSE; }
+  virtual OBJECT* get_object() const { return NULL; }
+  virtual OBJECT_REF<OBJECT> get_object_ref() { return OBJECT_REF<OBJECT>(); }
+};
+
+template<typename T>
+class NON_CLASS_EXCEPTION : public EXCEPTION_BASE {
+public:
+  struct exception_struct {
+    T* val_ptr;
+    size_t ref_count;
+  };
+private:
+  exception_struct* exc_ptr;
+  const char* type_name;
+  NON_CLASS_EXCEPTION operator=(const NON_CLASS_EXCEPTION&); // assignment disabled
+public:
+  NON_CLASS_EXCEPTION(T* p_value, const char* p_type_name)
+  : EXCEPTION_BASE(), exc_ptr(new exception_struct), type_name(p_type_name) {
+    exc_ptr->val_ptr = p_value;
+    exc_ptr->ref_count = 1;
+    exception_log = (TTCN_Logger::begin_event_log2str(),
+      p_value->log(), TTCN_Logger::end_event_log2str());
   }
-  ~EXCEPTION_BASE() {
-    delete exc_types;
+  NON_CLASS_EXCEPTION(const NON_CLASS_EXCEPTION& p)
+    : EXCEPTION_BASE(p), exc_ptr(p.exc_ptr), type_name(p.type_name) {
+    ++exc_ptr->ref_count;
   }
-  CHARSTRING get_log() const { return exception_log; }
-  boolean has_type(const char* type_name) const {
-    for (size_t i = 0; i < nof_types; ++i) {
-      if (strcmp(exc_types[i], type_name) == 0) {
-        return TRUE;
-      }
+  virtual ~NON_CLASS_EXCEPTION() {
+    --exc_ptr->ref_count;
+    if (exc_ptr->ref_count == 0) {
+      delete exc_ptr->val_ptr;
+      delete exc_ptr;
     }
-    return FALSE;
+  }
+  T& operator()() { return *exc_ptr->val_ptr; }
+  virtual boolean is_type(const char* p_type_name) const {
+    return strcmp(p_type_name, type_name) == 0;
   }
 };
 
 template<typename T>
-class EXCEPTION : public EXCEPTION_BASE {
+class CLASS_EXCEPTION : public EXCEPTION_BASE {
 public:
   struct exception_struct {
-    T* val_ptr;
+    OBJECT_REF<T>* val_ptr;
     size_t ref_count;
   };
 private:
   exception_struct* exc_ptr;
-  EXCEPTION operator=(const EXCEPTION&); // assignment disabled
+  CLASS_EXCEPTION operator=(const CLASS_EXCEPTION&); // assignment disabled
 public:
-  EXCEPTION(T* p_value, const char** p_exc_types, size_t p_nof_types)
-    : EXCEPTION_BASE(p_exc_types, p_nof_types), exc_ptr(new exception_struct) {
+  CLASS_EXCEPTION(OBJECT_REF<T>* p_value)
+  : EXCEPTION_BASE(), exc_ptr(new exception_struct) {
     exc_ptr->val_ptr = p_value;
     exc_ptr->ref_count = 1;
     exception_log = (TTCN_Logger::begin_event_log2str(),
       p_value->log(), TTCN_Logger::end_event_log2str());
   }
-  EXCEPTION(const EXCEPTION& p)
+  CLASS_EXCEPTION(const CLASS_EXCEPTION& p)
     : EXCEPTION_BASE(p), exc_ptr(p.exc_ptr) {
     ++exc_ptr->ref_count;
   }
-  ~EXCEPTION() {
+  virtual ~CLASS_EXCEPTION() {
     --exc_ptr->ref_count;
     if (exc_ptr->ref_count == 0) {
       delete exc_ptr->val_ptr;
       delete exc_ptr;
     }
   }
-  T& operator()() { return *exc_ptr->val_ptr; }
+  OBJECT_REF<T>& operator()() { return *exc_ptr->val_ptr; }
+  virtual boolean is_class() const { return TRUE; }
+  virtual OBJECT* get_object() const {
+    return **exc_ptr->val_ptr;
+  }
+  virtual OBJECT_REF<OBJECT> get_object_ref() {
+    return exc_ptr->val_ptr->template cast_to<OBJECT>();
+  }
 };
 
+
 #if __cplusplus >= 201103L
-class FINALLY
+class FINALLY_CPP11
 {
   std::function<void(void)> functor;
 public:
-  FINALLY(const std::function<void(void)> &p_functor) : functor(p_functor) {}
-  ~FINALLY()
+  FINALLY_CPP11(const std::function<void(void)> &p_functor) : functor(p_functor) {}
+  ~FINALLY_CPP11()
   {
     try {
       functor();
diff --git a/regression_test/oop/exceptions.ttcn b/regression_test/oop/exceptions.ttcn
index 2a6319a4c..61ef81d7e 100644
--- a/regression_test/oop/exceptions.ttcn
+++ b/regression_test/oop/exceptions.ttcn
@@ -76,6 +76,10 @@ function test_block(in integer p_exception_type, in charstring p_dummy := "a") r
       case (8) {
         raise Class2.create;
       }
+      case (9) {
+        var object x := Class.create();
+        raise x;
+      }
     }
     // otherwise don't raise anything
   }
@@ -129,12 +133,12 @@ function test_block(in integer p_exception_type, in charstring p_dummy := "a") r
 }
 
 testcase tc_exceptions_block() runs on CT {
-  for (var integer i := 1; i <= 8; i := i + 1) {
+  for (var integer i := 1; i <= 9; i := i + 1) {
     test_block(i);
   }
   test_block(200);
   
-  if (cv_finally_counter != 9) {
+  if (cv_finally_counter != 10) {
     setverdict(fail, "Invalid 'finally' execution count: ", cv_finally_counter);
   }
 }
-- 
GitLab