From 9da215e2775b57dfe0070f51d73928f1cfe66a4f Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Thu, 12 Nov 2020 15:36:14 +0100
Subject: [PATCH] Implemented the external class skeleton generator (bug
 563718)

Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
Change-Id: Id96880b45c5c805e012bd6cda8c65c8ea0ebc5a6
---
 compiler2/AST.cc                              |   2 +
 compiler2/compiler.1                          |   7 +-
 compiler2/main.cc                             |   2 +-
 compiler2/makefile.c                          |   2 +-
 compiler2/ttcn3/AST_ttcn3.cc                  |  56 +++-
 compiler2/ttcn3/Ttcnstuff.cc                  | 302 +++++++++++++-----
 compiler2/ttcn3/Ttcnstuff.hh                  |   1 +
 compiler2/ttcn3/compiler.y                    |   5 +
 .../Semantic_Analyser/oop/oop_SE.ttcn         |  13 +-
 9 files changed, 282 insertions(+), 108 deletions(-)

diff --git a/compiler2/AST.cc b/compiler2/AST.cc
index 310d8b663..266e518e5 100644
--- a/compiler2/AST.cc
+++ b/compiler2/AST.cc
@@ -1863,6 +1863,8 @@ namespace Common {
       return "timer parameter";
     case A_PAR_PORT:
       return "port parameter";
+    case A_CONSTRUCTOR:
+      return "constructor";
     default:
       return "<unknown>";
     }
diff --git a/compiler2/compiler.1 b/compiler2/compiler.1
index d19479130..1322c773e 100644
--- a/compiler2/compiler.1
+++ b/compiler2/compiler.1
@@ -269,8 +269,11 @@ context information.
 .B \-t
 Generates
 .I Test Port
-skeleton header and source files for all port types that can be found in
-TTCN-3 modules. Existing Test Port files will not be overwritten unless the
+and
+.I External Class
+skeleton header and source files for all port types and external class types
+that can be found in the input TTCN-3 modules. Existing Test Port and External Class
+files will not be overwritten unless the
 .B \-f
 option is used.
 .TP
diff --git a/compiler2/main.cc b/compiler2/main.cc
index 4ff27cd1b..c392136ac 100644
--- a/compiler2/main.cc
+++ b/compiler2/main.cc
@@ -435,7 +435,7 @@ static void usage()
     "	-R:		use function test runtime (TITAN_RUNTIME_2)\n"
     "	-s:		parse and semantic check only (no code generation)\n"
     "	-S:		suppress context information\n"
-    "	-t:		generate Test Port skeleton\n"
+    "	-t:		generate skeletons for test ports and external classes\n"
     "	-u:		duplicate underscores in file names\n"
     "	-U none|type|'number':	select code splitting mode for the generated C++ code\n"
     "	-V verb_level:	set verbosity level bitmask (decimal)\n"
diff --git a/compiler2/makefile.c b/compiler2/makefile.c
index 8dcfc02dd..89d609335 100644
--- a/compiler2/makefile.c
+++ b/compiler2/makefile.c
@@ -1974,7 +1974,7 @@ static void print_makefile(struct makefile_struct *makefile)
       "# - make archive        Archives all source files.\n"
       "# - make check          Checks the semantics of TTCN-3 and ASN.1"
       "modules.\n"       
-      "# - make port           Generates port skeletons.\n"
+      "# - make port           Generates test port and external class skeletons.\n"
       "%s" // clean:
       "%s" //clean-all
       "# - make compile        Translates TTCN-3 and ASN.1 modules to C++.\n"
diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index 4a7565b1c..77a2dfeb4 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -2306,7 +2306,7 @@ namespace Ttcn {
       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,
-          "%s:\n", vis == PUBLIC ? "public" : (vis == PRIVATE ? "private" :
+          "\n%s:\n", vis == PUBLIC ? "public" : (vis == PRIVATE ? "private" :
           "protected"));
       }
       ass_v[i]->generate_code(target);
@@ -7969,6 +7969,14 @@ namespace Ttcn {
         body);
       Free(body);
     }
+    else if (in_class && my_scope->get_scope_class()->is_external()) {
+      target->source.methods = mputprintf(target->source.methods,
+        "%s %s::%s(%s)\n"
+        "{\n\n"
+        "}\n\n", return_type_str,
+        my_scope->get_scope_class()->get_id()->get_name().c_str(),
+        genname_str, formal_par_list);
+    }
 
     Free(formal_par_list);
 
@@ -8818,11 +8826,13 @@ namespace Ttcn {
     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 || block == NULL) {
+    if (p_fp_list == NULL) {
       FATAL_ERROR("Def_Constructor::Def_Constructor");
     }
     fp_list->set_my_def(this);
-    block->set_my_def(this);
+    if (block != NULL) {
+      block->set_my_def(this);
+    }
   }
   
   Def_Constructor::~Def_Constructor()
@@ -8844,7 +8854,9 @@ namespace Ttcn {
     if (base_call != NULL) {
       base_call->set_fullname(p_fullname + ".<base_call>");
     }
-    block->set_fullname(p_fullname + ".<statement_block>");
+    if (block != NULL) {
+      block->set_fullname(p_fullname + ".<statement_block>");
+    }
   }
 
   void Def_Constructor::set_my_scope(Scope* p_scope)
@@ -8858,7 +8870,9 @@ namespace Ttcn {
     if (base_call != NULL) {
       base_call->set_my_scope(fp_list);
     }
-    block->set_my_scope(fp_list);
+    if (block != NULL) {
+      block->set_my_scope(fp_list);
+    }
   }
 
   FormalParList* Def_Constructor::get_FormalParList()
@@ -8905,7 +8919,7 @@ namespace Ttcn {
         }
       }
     }
-    else if (base_class != NULL) {
+    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()) {
@@ -8913,14 +8927,24 @@ namespace Ttcn {
       }
     }
     
-    block->chk();
+    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());
-      block->set_code_section(GovernedSimple::CS_INLINE);
+      if (block != NULL) {
+        block->set_code_section(GovernedSimple::CS_INLINE);
+      }
     }
   }
   
@@ -8931,19 +8955,23 @@ namespace Ttcn {
         target->temp.constructor_block);
     }
     
-    char* block_gen_str = block->generate_code(memptystr(),
-      target->header.global_vars, target->source.global_vars);
+    char* block_gen_str = block != NULL ? block->generate_code(memptystr(),
+      target->header.global_vars, target->source.global_vars) : NULL;
     fp_list->generate_code_defval(target);
-    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);
+    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);
+    }
     Free(block_gen_str);
   }
   
   void Def_Constructor::set_parent_path(WithAttribPath* p_path)
   {
     Definition::set_parent_path(p_path);
-    block->set_parent_path(w_attrib_path);
+    if (block != NULL) {
+      block->set_parent_path(w_attrib_path);
+    }
   }
 
   // =================================
diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc
index 45e3eae0f..51d0c53c2 100644
--- a/compiler2/ttcn3/Ttcnstuff.cc
+++ b/compiler2/ttcn3/Ttcnstuff.cc
@@ -26,6 +26,9 @@
 #include "Statement.hh"
 #include "Templatestuff.hh"
 #include "TtcnTemplate.hh"
+#include "../../common/path.h"
+#include "../../common/userinfo.h"
+#include "../../common/version_internal.h"
 
 // implemented in coding_attrib_p.y
 extern Ttcn::ExtensionAttributes * parse_extattributes(
@@ -3476,7 +3479,7 @@ namespace Ttcn {
       // create a default constructor
       Reference* base_call = NULL;
       FormalParList* fp_list = NULL;
-      if (base_class != NULL) {
+      if (!external && base_class != NULL) {
         Def_Constructor* base_constructor = base_class->get_constructor();
         if (base_constructor != NULL) {
           FormalParList* base_fp_list = base_constructor->get_FormalParList();
@@ -3499,36 +3502,39 @@ namespace Ttcn {
       if (fp_list == NULL) {
         fp_list = new FormalParList;
       }
-      StatementBlock* block = new StatementBlock();
-      for (size_t i = 0; i < members->get_nof_asss(); ++i) {
-        // note: the Definitions class rearranges its element alphabetically;
-        // here the members must be accessed in their original order
-        Common::Assignment* member = members->get_ass_byIndex(i, false);
-        bool is_template = false;
-        switch (member->get_asstype()) {
-        case Common::Assignment::A_TEMPLATE:
-        case Common::Assignment::A_VAR_TEMPLATE:
-          is_template = true;
-          // no break
-        case Common::Assignment::A_CONST:
-        case Common::Assignment::A_VAR: {
-          // add a formal parameter for this member
-          Common::Identifier* id = member->get_id().clone();
-          FormalPar* fp = new FormalPar(is_template ?
-            Common::Assignment::A_PAR_TEMPL_IN : Common::Assignment::A_PAR_VAL_IN,
-            member->get_Type()->clone(), id, NULL);
-          fp_list->add_fp(fp);
-          // add a statement, that assigns the parameter's value to the member
-          Reference* ref_lhs = new Reference(NULL, id->clone(), Ref_simple::REF_THIS);
-          Reference* ref_rhs = new Reference(NULL, id->clone());
-          Common::Value* val_rhs = new Value(Common::Value::V_REFD, ref_rhs);
-          Template* temp_rhs = new Template(val_rhs);
-          Assignment* par_ass = new Assignment(ref_lhs, temp_rhs);
-          Statement* stmt = new Statement(Statement::S_ASSIGNMENT, par_ass);
-          block->add_stmt(stmt);
-          break; }
-        default:
-          break;
+      StatementBlock* block = NULL;
+      if (!external) {
+        block = new StatementBlock();
+        for (size_t i = 0; i < members->get_nof_asss(); ++i) {
+          // note: the Definitions class rearranges its element alphabetically;
+          // here the members must be accessed in their original order
+          Common::Assignment* member = members->get_ass_byIndex(i, false);
+          bool is_template = false;
+          switch (member->get_asstype()) {
+          case Common::Assignment::A_TEMPLATE:
+          case Common::Assignment::A_VAR_TEMPLATE:
+            is_template = true;
+            // no break
+          case Common::Assignment::A_CONST:
+          case Common::Assignment::A_VAR: {
+            // add a formal parameter for this member
+            Common::Identifier* id = member->get_id().clone();
+            FormalPar* fp = new FormalPar(is_template ?
+              Common::Assignment::A_PAR_TEMPL_IN : Common::Assignment::A_PAR_VAL_IN,
+              member->get_Type()->clone(), id, NULL);
+            fp_list->add_fp(fp);
+            // add a statement, that assigns the parameter's value to the member
+            Reference* ref_lhs = new Reference(NULL, id->clone(), Ref_simple::REF_THIS);
+            Reference* ref_rhs = new Reference(NULL, id->clone());
+            Common::Value* val_rhs = new Value(Common::Value::V_REFD, ref_rhs);
+            Template* temp_rhs = new Template(val_rhs);
+            Assignment* par_ass = new Assignment(ref_lhs, temp_rhs);
+            Statement* stmt = new Statement(Statement::S_ASSIGNMENT, par_ass);
+            block->add_stmt(stmt);
+            break; }
+          default:
+            break;
+          }
         }
       }
       constructor = new Def_Constructor(fp_list, base_call, block);
@@ -3549,6 +3555,7 @@ namespace Ttcn {
         case Common::Assignment::A_EXT_FUNCTION:
         case Common::Assignment::A_EXT_FUNCTION_RVAL:
         case Common::Assignment::A_EXT_FUNCTION_RTEMP:
+        case Common::Assignment::A_CONSTRUCTOR:
           // OK
           break;
         default:
@@ -3649,28 +3656,34 @@ namespace Ttcn {
     }
     target->header.class_decls = mputprintf(target->header.class_decls,
       "class %s;\n", class_id->get_name().c_str());
-    if (!external) {
+    if (!external || generate_skeleton) {
       string base_type_name = base_type != NULL ?
         base_type->get_type_refd_last()->get_genname_own(this) : string("OBJECT");
+      output_struct* local_struct;
+      if (external) {
+        local_struct = new output_struct;
+        Code::init_output(local_struct, TRUE);
+      }
+      else {
+        local_struct = target;
+      }
       
-      target->header.class_defs = mputprintf(target->header.class_defs,
+      local_struct->header.class_defs = mputprintf(local_struct->header.class_defs,
         "class %s : public %s {\n",
         class_id->get_name().c_str(), base_type_name.c_str());
       
       // class name
-      target->header.class_defs = mputprintf(target->header.class_defs,
+      local_struct->header.class_defs = mputprintf(local_struct->header.class_defs,
         "public:\n"
-        "static const char* class_name() { return \"%s\"; }\n\n",
+        "static const char* class_name() { return \"%s\"; }\n",
         class_id->get_dispname().c_str());
       
       // members
-      members->generate_code(target);
+      members->generate_code(local_struct);
       if (default_constructor) {
-        target->source.methods = mputstr(target->source.methods,
-          "/* default constructor */\n");
         // code has not been generated for the constructor yet, since it's not
         // in 'members' in this case
-        constructor->generate_code(target);
+        constructor->generate_code(local_struct);
       }
       
       // constructor
@@ -3684,16 +3697,17 @@ namespace Ttcn {
       // generate code for the base call first, so the formal parameter list
       // knows which parameters are used and which aren't
       formal_par_list_str = constructor->get_FormalParList()->generate_code(
-        memptystr());
+        memptystr(), external ? constructor->get_FormalParList()->get_nof_fps() : 0);
       if (base_call_expr.expr == NULL) {
         base_call_expr.expr = mprintf("%s()", base_type_name.c_str());
       }
-      target->header.class_defs = mputprintf(target->header.class_defs,
-        "public:\n"
-        "%s(%s);\n",
+      local_struct->header.class_defs = mputprintf(local_struct->header.class_defs,
+        "\npublic:\n%s"
+        "%s(%s);\n\n",
+        default_constructor ? "/* default constructor */\n" : "",
         class_id->get_name().c_str(),
         formal_par_list_str != NULL ? formal_par_list_str : "");
-      target->source.methods = mputprintf(target->source.methods,
+      local_struct->source.methods = mputprintf(local_struct->source.methods,
         "%s::%s(%s)\n"
         ": %s",
         class_id->get_name().c_str(), class_id->get_name().c_str(),
@@ -3701,46 +3715,57 @@ namespace Ttcn {
         base_call_expr.expr);
       Free(formal_par_list_str);
       Code::free_expr(&base_call_expr);
-      if (target->temp.constructor_init != NULL) {
-        target->source.methods = mputstr(target->source.methods,
-          target->temp.constructor_init);
-        Free(target->temp.constructor_init);
-        target->temp.constructor_init = NULL;
-      }
-      target->source.methods = mputstr(target->source.methods, "\n{\n");
-      if (target->temp.constructor_preamble != NULL ||
-          target->temp.constructor_block != NULL) {
-        target->source.methods = create_location_object(
-          target->source.methods, "FUNCTION", class_id->get_name().c_str());
-        target->source.methods = mputstr(target->source.methods,
-          target->temp.constructor_preamble);
-        target->source.methods = mputstr(target->source.methods,
-          target->temp.constructor_block);
-        Free(target->temp.constructor_preamble);
-        Free(target->temp.constructor_block);
-        target->temp.constructor_preamble = NULL;
-        target->temp.constructor_block = NULL;
-      }
-      target->source.methods = mputstr(target->source.methods, "}\n\n");
+      if (local_struct->temp.constructor_init != NULL) {
+        local_struct->source.methods = mputstr(local_struct->source.methods,
+          local_struct->temp.constructor_init);
+        Free(local_struct->temp.constructor_init);
+        local_struct->temp.constructor_init = NULL;
+      }
+      local_struct->source.methods = mputstr(local_struct->source.methods, "\n{\n");
+      if (local_struct->temp.constructor_preamble != NULL ||
+          local_struct->temp.constructor_block != NULL) {
+        local_struct->source.methods = create_location_object(
+          local_struct->source.methods, "FUNCTION", class_id->get_name().c_str());
+        local_struct->source.methods = mputstr(local_struct->source.methods,
+          local_struct->temp.constructor_preamble);
+        local_struct->source.methods = mputstr(local_struct->source.methods,
+          local_struct->temp.constructor_block);
+        Free(local_struct->temp.constructor_preamble);
+        Free(local_struct->temp.constructor_block);
+        local_struct->temp.constructor_preamble = NULL;
+        local_struct->temp.constructor_block = NULL;
+      }
+      else if (external) {
+        local_struct->source.methods = mputc(local_struct->source.methods, '\n');
+      }
+      local_struct->source.methods = mputstr(local_struct->source.methods, "}\n\n");
 
       // destructor
-      target->header.class_defs = mputprintf(target->header.class_defs,
+      bool desctructor_body = finally_block != NULL || external;
+      local_struct->header.class_defs = mputprintf(local_struct->header.class_defs,
         "public:\n"
-        "virtual ~%s()%s\n",
-        class_id->get_name().c_str(), finally_block != NULL ? ";" : " { }");
-      if (finally_block != NULL) {
-        target->source.methods = mputprintf(target->source.methods,
+        "virtual ~%s()%s\n\n",
+        class_id->get_name().c_str(), desctructor_body ? ";" : " { }");
+      if (desctructor_body) {
+        local_struct->source.methods = mputprintf(local_struct->source.methods,
           "%s::~%s()\n"
           "{\n", class_id->get_name().c_str(), class_id->get_name().c_str());
         char* destructor_str = mprintf("~%s", class_id->get_name().c_str());
-        target->source.methods = finally_block->create_location_object(
-          target->source.methods, "FUNCTION", destructor_str);
+        if (finally_block != NULL) {
+          local_struct->source.methods = finally_block->create_location_object(
+            local_struct->source.methods, "FUNCTION", destructor_str);
+        }
         Free(destructor_str);
-        target->source.methods = mputstr(target->source.methods, "try {\n");
-        target->source.methods = finally_block->generate_code(
-          target->source.methods, target->header.global_vars,
-          target->source.global_vars);
-        target->source.methods = mputprintf(target->source.methods,
+        local_struct->source.methods = mputstr(local_struct->source.methods, "try {\n");
+        if (finally_block != NULL) {
+          local_struct->source.methods = finally_block->generate_code(
+            local_struct->source.methods, local_struct->header.global_vars,
+            local_struct->source.global_vars);
+        }
+        else {
+          local_struct->source.methods = mputc(local_struct->source.methods, '\n');
+        }
+        local_struct->source.methods = mputprintf(local_struct->source.methods,
           "} catch (...) {\n"
           "fprintf(stderr, \"Unhandled exception or dynamic test case error in "
           "destructor of class `%s'\");\n"
@@ -3751,17 +3776,17 @@ namespace Ttcn {
       
       // logging function (similar to logging a record value, but with the 
       // class name and the base class' log at the beginning)
-      target->header.class_defs = mputstr(target->header.class_defs,
+      local_struct->header.class_defs = mputstr(local_struct->header.class_defs,
         "public:\n"
         "virtual void log() const;\n");
-      target->source.methods = mputprintf(target->source.methods,
+      local_struct->source.methods = mputprintf(local_struct->source.methods,
         "void %s::log() const\n"
         "{\n"
         "TTCN_Logger::log_event_str(\"%s: { \");\n",
         class_id->get_name().c_str(), class_id->get_dispname().c_str());
       bool first_logged = false;
       if (base_type != NULL) {
-        target->source.methods = mputprintf(target->source.methods,
+        local_struct->source.methods = mputprintf(local_struct->source.methods,
           "%s::log();\n",
           base_type_name.c_str());
         first_logged = true;
@@ -3775,10 +3800,10 @@ namespace Ttcn {
         case Common::Assignment::A_VAR_TEMPLATE:
         case Common::Assignment::A_TIMER: // ?
           if (first_logged) {
-            target->source.methods = mputstr(target->source.methods,
+            local_struct->source.methods = mputstr(local_struct->source.methods,
               "TTCN_Logger::log_event_str(\", \");\n");
           }
-          target->source.methods = mputprintf(target->source.methods,
+          local_struct->source.methods = mputprintf(local_struct->source.methods,
             "TTCN_Logger::log_event_str(\"%s := \");\n"
             "%s.log();\n",
             member->get_id().get_dispname().c_str(), member->get_id().get_name().c_str());
@@ -3788,13 +3813,19 @@ namespace Ttcn {
           break; // don't log anything for methods
         }
       }
-      target->source.methods = mputstr(target->source.methods,
+      local_struct->source.methods = mputstr(local_struct->source.methods,
         "TTCN_Logger::log_event_str(\" }\");\n"
         "}\n\n");
       
-      target->header.class_defs = mputstr(target->header.class_defs, "};\n\n");
+      local_struct->header.class_defs = mputstr(local_struct->header.class_defs, "};\n\n");
+
+      if (external) {
+        generate_class_skeleton(local_struct);
+        Code::free_output(local_struct);
+        delete local_struct;
+      }
     }
-    else { // external class
+    if (external) {
       target->header.class_defs = mputprintf(target->header.class_defs,
         "#include \"%s.hh\"\n\n",
         duplicate_underscores ? class_id->get_name().c_str() :
@@ -3802,4 +3833,105 @@ namespace Ttcn {
     }
   }
 
+#undef COMMENT_PREFIX
+#define COMMENT_PREFIX "// "
+
+  void ClassTypeBody::generate_class_skeleton(output_struct* target)
+  {
+    string file_prefix = duplicate_underscores ? class_id->get_name() :
+      class_id->get_dispname();
+
+    DEBUG(1, "Generating external class skeleton for class type `%s' ...",
+      class_id->get_dispname().c_str());
+
+    string header_name;
+    string source_name;
+    string base_header_include;
+    if (output_dir != NULL) {
+      header_name = string(output_dir) + string("/");
+      source_name = string(output_dir) + string("/");
+    }
+    header_name += file_prefix + string(".hh");
+    source_name += file_prefix + string(".cc");
+
+    if (base_class != NULL) {
+      base_header_include = string("#include \"");
+      base_header_include += duplicate_underscores ? base_class->class_id->get_name() :
+          base_class->class_id->get_dispname();
+      base_header_include += string(".hh\"\n\n");
+    }
+
+    char* user_info = NULL;
+    if (disable_user_info == FALSE) {
+      user_info = get_user_info();
+    } else {
+      user_info = mputstr(user_info, "The generation of user and time information were disabled by the -D flag.\n");
+    }
+
+    if (force_overwrite || get_path_status(header_name.c_str()) == PS_NONEXISTENT) {
+      FILE *fp = fopen(header_name.c_str(), "w");
+      if (fp == NULL) {
+        ERROR("Cannot open output external class skeleton header file `%s' for "
+          "writing: %s", header_name.c_str(), strerror(errno));
+        exit(EXIT_FAILURE);
+      }
+      fprintf(fp,
+        "// This external class skeleton header file was generated by the\n"
+        "// TTCN-3 Compiler of the TTCN-3 Test Executor version "
+        PRODUCT_NUMBER "\n"
+        "// %s%s\n"
+        COPYRIGHT_STRING "\n\n"
+        "// You may modify this file. Add your attributes and prototypes of your\n"
+        "// member functions here.\n\n"
+        "#ifndef %s_HH\n"
+        "#define %s_HH\n\n"
+        "%s%s"
+        "#endif\n",
+        disable_user_info == FALSE ? "for " : "", user_info,
+        class_id->get_name().c_str(), class_id->get_name().c_str(),
+        base_header_include.c_str(), target->header.class_defs);
+      fclose(fp);
+      NOTIFY("External class skeleton header file `%s' was generated for class "
+        "type `%s'.", header_name.c_str(), class_id->get_dispname().c_str());
+    }
+    else {
+      DEBUG(1, "External class header file `%s' already exists. It was not "
+        "overwritten.", header_name.c_str());
+    }
+
+    if (force_overwrite || get_path_status(source_name.c_str()) == PS_NONEXISTENT) {
+      FILE *fp = fopen(source_name.c_str(), "w");
+      if (fp == NULL) {
+        ERROR("Cannot open output external class skeleton source file `%s' for "
+          "writing: %s", source_name.c_str(), strerror(errno));
+        exit(EXIT_FAILURE);
+      }
+      fprintf(fp,
+        "// This external class skeleton source file was generated by the\n"
+        "// TTCN-3 Compiler of the TTCN-3 Test Executor version "
+        PRODUCT_NUMBER "\n"
+        "// %s%s\n"
+        COPYRIGHT_STRING "\n\n"
+        "// You may modify this file. Complete the bodies of empty functions and\n"
+        "// add your member functions here.\n\n"
+        "#include <TTCN3.hh>\n\n"
+        "namespace %s {\n\n"
+        "#include \"%s.hh\"\n\n"
+        "%s\n"
+        "} /* end of namespace */\n\n",
+        disable_user_info == FALSE ? "for " : "", user_info,
+        get_scope_mod_gen()->get_modid().get_name().c_str(), class_id->get_dispname().c_str(),
+        target->source.methods);
+      fclose(fp);
+      NOTIFY("External class skeleton source file `%s' was generated for class "
+        "type `%s'.", source_name.c_str(), file_prefix.c_str());
+    }
+    else {
+      DEBUG(1, "External class source file `%s' already exists. It was not "
+        "overwritten.", source_name.c_str());
+    }
+
+    Free(user_info);
+  }
+
 } // namespace Ttcn
diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh
index c9facafbd..af7191e91 100644
--- a/compiler2/ttcn3/Ttcnstuff.hh
+++ b/compiler2/ttcn3/Ttcnstuff.hh
@@ -821,6 +821,7 @@ public:
   void chk_recursions(ReferenceChain& refch);
   
   void generate_code(output_struct* target);
+  void generate_class_skeleton(output_struct* target);
 };
 
 }
diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y
index a82b1afdc..b884110f2 100644
--- a/compiler2/ttcn3/compiler.y
+++ b/compiler2/ttcn3/compiler.y
@@ -3709,6 +3709,11 @@ ConstructorDef:
     $$ = new Def_Constructor($3, $5, $6);
     $$->set_location(infile, @$);
   }
+| CreateKeyword '(' optFunctionFormalParList ')'
+  {
+	$$ = new Def_Constructor($3, NULL, NULL);
+    $$->set_location(infile, @$);
+  }
 ;
 
 optBaseConstructorCall:
diff --git a/function_test/Semantic_Analyser/oop/oop_SE.ttcn b/function_test/Semantic_Analyser/oop/oop_SE.ttcn
index cfd560a45..5a23e7808 100644
--- a/function_test/Semantic_Analyser/oop/oop_SE.ttcn
+++ b/function_test/Semantic_Analyser/oop/oop_SE.ttcn
@@ -430,19 +430,21 @@ function f_default() { //^In function definition//
 
 
 type external class C40 {
+  create(in integer x);
   function f1(in integer p) return octetstring;
   external function f2();
 }
 
-type external class C41 { //^In type definition//
+type external class C41 extends C40 { //^In type definition//
+  create();
   const charstring c := "a"; //An external class cannot contain a constant//
   template charstring t := "b"; //An external class cannot contain a template//
   var charstring v; //An external class cannot contain a variable//
   var template charstring vt; //An external class cannot contain a template variable//
   timer tmr; //An external class cannot contain a timer//
-  function f1() { } //An external class cannot contain a function//
-  function f2(in charstring x) return charstring { return x; } //An external class cannot contain a function//
-  function f3() return template charstring { return ?; } //An external class cannot contain a function//
+  function g1() { } //An external class cannot contain a function//
+  function g2(in charstring x) return charstring { return x; } //An external class cannot contain a function//
+  function g3() return template charstring { return ?; } //An external class cannot contain a function//
 }
 
 type external class @abstract C42 { //^In type definition// //External classes cannot be abstract//
@@ -451,8 +453,9 @@ type external class @abstract C42 { //^In type definition// //External classes c
 
 type external class C43 extends C39 { } //^In type definition// //^In superclass definition// //An external class cannot extend an internal class//
 
-type class C44 extends C40 {
+type class C44 extends C40 { //^In type definition//
   external function f();
+  create(); //^In constructor definition// //Missing super-constructor call// //Missing constructor body//
 }
 
 type class C45 { //^In type definition//
-- 
GitLab