From 95745b3f66c5db0008729435b4ea07cb8b31812b Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Thu, 10 Oct 2019 17:31:34 +0200
Subject: [PATCH] Implemented object-oriented features - stage 1 (bug 552011)

Change-Id: Id2222ae91a23b3934e7c175aa163a3eec6f72cad
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 compiler2/AST.hh                     |   1 +
 compiler2/Code.cc                    |  18 +-
 compiler2/Code.hh                    |   2 +-
 compiler2/Setting.cc                 |   9 +
 compiler2/Setting.hh                 |   3 +
 compiler2/Type.cc                    |  52 ++++-
 compiler2/Type.hh                    |  10 +-
 compiler2/Type_chk.cc                |   3 +
 compiler2/Type_codegen.cc            |  47 ++--
 compiler2/asn1/AST_asn1.cc           |   2 +-
 compiler2/asn1/Object.cc             |   4 +-
 compiler2/main.cc                    |  12 +-
 compiler2/main.hh                    |   2 +-
 compiler2/ttcn3/AST_ttcn3.cc         | 336 +++++++++++++++++++++------
 compiler2/ttcn3/AST_ttcn3.hh         |  52 +++++
 compiler2/ttcn3/Ttcnstuff.cc         | 331 ++++++++++++++++++++++++--
 compiler2/ttcn3/Ttcnstuff.hh         |  41 ++++
 compiler2/ttcn3/compiler.h           |   4 +
 compiler2/ttcn3/compiler.l           |  26 +++
 compiler2/ttcn3/compiler.y           | 158 ++++++++++++-
 core/Makefile                        |   2 +-
 core/OOP.hh                          | 149 ++++++++++++
 core/TTCN3.hh                        |   1 +
 regression_test/oop/.gitignore       |   5 +
 regression_test/oop/ExternalClass.hh |  29 +++
 regression_test/oop/Makefile         |  55 +++++
 regression_test/oop/oop.cfg          |  24 ++
 regression_test/oop/oop.ttcn         | 121 ++++++++++
 28 files changed, 1375 insertions(+), 124 deletions(-)
 create mode 100644 core/OOP.hh
 create mode 100644 regression_test/oop/.gitignore
 create mode 100644 regression_test/oop/ExternalClass.hh
 create mode 100644 regression_test/oop/Makefile
 create mode 100644 regression_test/oop/oop.cfg
 create mode 100644 regression_test/oop/oop.ttcn

diff --git a/compiler2/AST.hh b/compiler2/AST.hh
index 6d5a6234b..dd0990488 100644
--- a/compiler2/AST.hh
+++ b/compiler2/AST.hh
@@ -525,6 +525,7 @@ namespace Common {
       A_EXT_FUNCTION_RTEMP, /**< ext. func that returns a template (TTCN-3) */
       A_ALTSTEP,        /**< altstep (TTCN-3) */
       A_TESTCASE,       /**< testcase (TTCN-3) */
+      A_CONSTRUCTOR,    /**< constructor (TTCN-3) */
       A_PAR_VAL,        /**< formal parameter (value) (TTCN-3) */
       A_PAR_VAL_IN,     /**< formal parameter (in value) (TTCN-3) */
       A_PAR_VAL_OUT,    /**< formal parameter (out value) (TTCN-3) */
diff --git a/compiler2/Code.cc b/compiler2/Code.cc
index 9270ede59..aaeca1094 100644
--- a/compiler2/Code.cc
+++ b/compiler2/Code.cc
@@ -82,6 +82,8 @@ namespace Common {
       output->intervals.static_conversion_function_bodies = NULL;
       output->intervals.static_function_bodies = NULL;
     }
+    output->temp.constructor_init = NULL;
+    output->temp.constructor = NULL;
   }
 
   void Code::merge_output(output_struct *dest, output_struct *src)
@@ -177,6 +179,8 @@ namespace Common {
     Free(output->intervals.function_bodies);
     Free(output->intervals.static_conversion_function_bodies);
     Free(output->intervals.static_function_bodies);
+    Free(output->temp.constructor_init);
+    Free(output->temp.constructor);
     init_output(output, TRUE);
   }
 
@@ -189,12 +193,16 @@ namespace Common {
     cdef->post = NULL;
   }
 
-  void Code::merge_cdef(output_struct *dest, const_def *cdef)
+  void Code::merge_cdef(output_struct *dest, const_def *cdef, boolean in_class)
   {
-    dest->header.global_vars = mputstr(dest->header.global_vars, cdef->decl);
-    dest->source.global_vars = mputstr(dest->source.global_vars, cdef->def);
-    dest->functions.pre_init = mputstr(dest->functions.pre_init, cdef->init);
-    dest->functions.post_init = mputstr(dest->functions.post_init, cdef->post);
+    char*& header = in_class ? dest->header.class_defs : dest->header.global_vars;
+    header = mputstr(header, cdef->decl);
+    char*& source = in_class ? dest->temp.constructor_init : dest->source.global_vars;
+    source = mputstr(source, cdef->def);
+    char*& pre_init = in_class ? dest->temp.constructor : dest->functions.pre_init;
+    pre_init = mputstr(pre_init, cdef->init);
+    char*& post_init = in_class ? dest->temp.constructor : dest->functions.post_init;
+    post_init = mputstr(post_init, cdef->post);
   }
 
   void Code::free_cdef(const_def *cdef)
diff --git a/compiler2/Code.hh b/compiler2/Code.hh
index 609880805..6466c352f 100644
--- a/compiler2/Code.hh
+++ b/compiler2/Code.hh
@@ -36,7 +36,7 @@ namespace Common {
     static void free_output(output_struct *output);
 
     static void init_cdef(const_def *cdef);
-    static void merge_cdef(output_struct *dest, const_def *cdef);
+    static void merge_cdef(output_struct *dest, const_def *cdef, boolean in_class = FALSE);
     static void free_cdef(const_def *cdef);
 
     static void init_expr(expression_struct *expr);
diff --git a/compiler2/Setting.cc b/compiler2/Setting.cc
index cdb91279e..cb532aea0 100644
--- a/compiler2/Setting.cc
+++ b/compiler2/Setting.cc
@@ -38,6 +38,7 @@
 #include "main.hh"
 #include "ttcn3/profiler.h"
 #include "ttcn3/Attributes.hh"
+#include "ttcn3/Ttcnstuff.hh"
 
 namespace Common {
 
@@ -604,6 +605,14 @@ namespace Common {
                      get_scope_name().c_str());
     return 0;
   }
+  
+  const Ttcn::ClassTypeBody* Scope::get_scope_class() const
+  {
+    if (parent_scope != NULL) {
+      return parent_scope->get_scope_class();
+    }
+    return NULL;
+  }
 
   bool Scope::has_ass_withId(const Identifier& p_id)
   {
diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh
index d140ddc6d..3f559255d 100644
--- a/compiler2/Setting.hh
+++ b/compiler2/Setting.hh
@@ -62,6 +62,7 @@ namespace Ttcn {
   class ErroneousDescriptors;
   class transparency_holder;
   class Statement;
+  class ClassTypeBody;
 } // namespace Ttcn
 
 namespace Common {
@@ -613,6 +614,8 @@ public:
      * Module. */
     virtual Module* get_scope_mod();
     virtual Module* get_scope_mod_gen();
+    virtual bool is_class_scope() const { return false; }
+    virtual const Ttcn::ClassTypeBody* get_scope_class() const;
     /** Returns the assignment referenced by \a p_ref. If no such
      * node, 0 is returned. */
     virtual Assignment* get_ass_bySRef(Ref_simple *p_ref) =0;
diff --git a/compiler2/Type.cc b/compiler2/Type.cc
index 6e8efcf1f..d41ef23c5 100644
--- a/compiler2/Type.cc
+++ b/compiler2/Type.cc
@@ -130,6 +130,7 @@ namespace Common {
   "altstep reference(TTCN-3)",                   // T_ALTSTEP
   "testcase reference(TTCN-3)",                  // T_TESTCASE
   "anytype(TTCN-3)",                             // T_ANYTYPE
+  "class(TTCN-3)"                                // T_CLASS
   };
 
   // =================================
@@ -579,6 +580,9 @@ namespace Common {
       u.fatref.returns_template = false;
       u.fatref.template_restriction = TR_NONE;
       break;
+    case T_CLASS:
+      u.class_ = p.u.class_->clone();
+      break;
     default:
       FATAL_ERROR("Type::Type()");
     } // switch
@@ -749,6 +753,9 @@ namespace Common {
       delete u.fatref.runs_on.ref;
       delete u.fatref.system.ref;
       break;
+    case T_CLASS:
+      delete u.class_;
+      break;
     default:
       FATAL_ERROR("Type::clean_up()");
     } // switch
@@ -1120,7 +1127,23 @@ namespace Common {
     u.fatref.returns_template = false;
     u.fatref.template_restriction = TR_NONE;
   }
-
+  
+  Type::Type(typetype_t p_tt, Ttcn::ClassTypeBody* p_class)
+  : Governor(S_T), typetype(p_tt)
+  {
+    init();
+    switch (typetype) {
+    case T_CLASS:
+      if (p_class == NULL) {
+        FATAL_ERROR("Type::Type");
+      }
+      u.class_ = p_class;
+      break;
+    default:
+      FATAL_ERROR("Type::Type");
+    }
+  }
+  
   Type::~Type()
   {
     clean_up();
@@ -1446,6 +1469,9 @@ namespace Common {
       if (u.fatref.system.ref)
         u.fatref.system.ref->set_fullname(p_fullname + ".<system_type>");
       break;
+    case T_CLASS:
+      u.class_->set_fullname(p_fullname);
+      break;
     default:
       break;
     } // switch
@@ -1522,6 +1548,9 @@ namespace Common {
       if (u.fatref.system.ref)
         u.fatref.system.ref->set_my_scope(p_scope);
       break;
+    case T_CLASS:
+      u.class_->set_my_scope(p_scope);
+      break;
     default:
       break;
     } // switch
@@ -3923,6 +3952,7 @@ namespace Common {
     case T_FUNCTION:
     case T_ALTSTEP:
     case T_TESTCASE:
+    case T_CLASS:
       return p_tt1 == p_tt2;
     case T_OSTR:
       return p_tt2==T_OSTR || (!p_is_asn11 && p_tt2==T_ANY);
@@ -4300,6 +4330,7 @@ namespace Common {
     case T_FUNCTION:
     case T_ALTSTEP:
     case T_TESTCASE:
+    case T_CLASS:
       // TODO: Compatibility.
       is_type_comp = ( t1 == t2 );
       break;
@@ -5222,6 +5253,7 @@ namespace Common {
     case T_FUNCTION:
     case T_ALTSTEP:
     case T_TESTCASE:
+    case T_CLASS:
       // user-defined structured types must be identical
       return t1 == t2;
     case T_ARRAY:
@@ -6034,6 +6066,14 @@ namespace Common {
       FATAL_ERROR("Type::Returns_template()");
     return u.fatref.returns_template;
   }
+  
+  Ttcn::ClassTypeBody* Type::get_class_type_body()
+  {
+    if (typetype != T_CLASS) {
+      FATAL_ERROR("Type::get_class_type_body()");
+    }
+    return u.class_;
+  }
 
   void Type::add_tag(Tag *p_tag)
   {
@@ -6680,6 +6720,7 @@ namespace Common {
           case T_FUNCTION:  // TTCN-3
           case T_ALTSTEP:   // TTCN-3
           case T_TESTCASE:  // TTCN-3
+          case T_CLASS:     // TTCN-3
             return memory.remember(t, ANSWER_NO);
 
           case T_UNDEF:
@@ -7387,7 +7428,8 @@ namespace Common {
     case T_SIGNATURE:
     case T_FUNCTION:
     case T_ALTSTEP:
-    case T_TESTCASE: {
+    case T_TESTCASE:
+    case T_CLASS: {
       // user-defined types
       // always use the qualified name (including module identifier)
       string ret_val(t_scope->get_scope_mod_gen()->get_modid().get_name());
@@ -7424,6 +7466,7 @@ namespace Common {
     case T_FUNCTION:
     case T_ALTSTEP:
     case T_TESTCASE:
+    case T_CLASS:
       return t->get_fullname();
     case T_ARRAY: {
       string dimensions(t->u.array.dimension->get_stringRepr());
@@ -8065,6 +8108,10 @@ namespace Common {
         u.fatref.system.ref->dump(level+2);
       }
       break;
+    case T_CLASS:
+      DEBUG(level,"Type: class");
+      u.class_->dump(level + 1);
+      break;
     default:
       DEBUG(level, "type (%d - %s)", typetype, const_cast<Type*>(this)->get_stringRepr().c_str());
     } // switch
@@ -8222,6 +8269,7 @@ namespace Common {
     switch (typetype) {
     case T_DEFAULT:
     case T_PORT:
+    case T_CLASS:
       return true;
     case T_FUNCTION:
     case T_ALTSTEP:
diff --git a/compiler2/Type.hh b/compiler2/Type.hh
index b8f524c79..d8a6de9da 100644
--- a/compiler2/Type.hh
+++ b/compiler2/Type.hh
@@ -77,6 +77,7 @@ namespace Ttcn {
   class PortTypeBody;
   class Def_Type;
   class Ref_pard;
+  class ClassTypeBody;
   
   /** Stores the modifier of an attribute */
   enum attribute_modifier_t {
@@ -199,6 +200,7 @@ namespace Common {
       T_ALTSTEP, /**< altstep reference (TTCN-3) */
       T_TESTCASE, /**< testcase reference (TTCN-3) */
       T_ANYTYPE, /**< anytype (TTCN-3) */
+      T_CLASS, /**< class (TTCN-3) */
       // WRITE new type before this line
       T_LAST
     };       //DO NOT FORGET to update type_as_string[] in Type.cc 
@@ -466,6 +468,7 @@ namespace Common {
         template_restriction_t template_restriction;
         bool is_startable;
       } fatref;
+      Ttcn::ClassTypeBody* class_;
     } u;
     static const char* type_as_string[];
     
@@ -587,6 +590,7 @@ namespace Common {
     /// Create a TTCN3 testcase
     Type(typetype_t p_tt,Ttcn::FormalParList *p_params,
         Ttcn::Reference* p_runs_on_ref, Ttcn::Reference *p_system_ref);
+    Type(typetype_t p_tt, Ttcn::ClassTypeBody* p_class);
     /** @} */
     virtual ~Type();
     /** This function must be called to clean up the pool types,
@@ -1165,6 +1169,8 @@ namespace Common {
     bool get_fat_runs_on_self();
     /** Applicable only if typetype == T_FUNCTION */
     bool get_returns_template();
+    /** Applicably only if typetype == T_CLASS */
+    Ttcn::ClassTypeBody* get_class_type_body();
 
     /** Retruns true if it is a tagged type.*/
     bool is_tagged() const {return tags!=0;}
@@ -1320,11 +1326,11 @@ namespace Common {
      * Value or Template object in the AST (e.g. in case of variables). */
     void generate_code_object(const_def *cdef, Scope* p_scope,
       const string& name, const char *prefix, bool is_template,
-      bool has_err_descr);
+      bool has_err_descr, bool in_class);
     /** Generates the declaration and definition of a C++ value or template
      * object governed by \a this into \a cdef based on the attributes of
      * \a p_setting. */
-    void generate_code_object(const_def *cdef, GovernedSimple *p_setting);
+    void generate_code_object(const_def *cdef, GovernedSimple *p_setting, bool in_class);
   private:
     virtual string create_stringRepr();
   public:
diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc
index 5ab6baaa7..5c302706e 100644
--- a/compiler2/Type_chk.cc
+++ b/compiler2/Type_chk.cc
@@ -186,6 +186,9 @@ void Type::chk()
   case T_TESTCASE:
     chk_Fat();
     break;
+  case T_CLASS:
+    u.class_->chk();
+    break;
   default:
     FATAL_ERROR("Type::chk()");
   } // switch
diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc
index 574cd68a6..0274341ff 100644
--- a/compiler2/Type_codegen.cc
+++ b/compiler2/Type_codegen.cc
@@ -101,6 +101,9 @@ void Type::generate_code(output_struct *target)
     case T_TESTCASE:
       generate_code_Fat(target);
       break;
+    case T_CLASS:
+      u.class_->generate_code(target);
+      break;
     default:
       generate_code_alias(target);
       break;
@@ -582,7 +585,7 @@ void Type::generate_code_xerdescriptor(output_struct* target)
       dfe_str = xerattrib->defaultValue_->get_genname_own().c_str();
       const_def cdef;
       Code::init_cdef(&cdef);
-      t->generate_code_object(&cdef, xerattrib->defaultValue_);
+      t->generate_code_object(&cdef, xerattrib->defaultValue_, false);
       // Generate the initialization of the dfe values in the post init function
       // because the module params are not initialized in the pre init function
       target->functions.post_init = xerattrib->defaultValue_->generate_code_init
@@ -1845,7 +1848,7 @@ void Type::generate_code_Se(output_struct *target)
       Value *defval = cf->get_defval();
       const_def cdef;
       Code::init_cdef(&cdef);
-      type->generate_code_object(&cdef, defval);
+      type->generate_code_object(&cdef, defval, false);
       cdef.init = defval->generate_code_init
         (cdef.init, defval->get_lhs_name().c_str());
       Code::merge_cdef(target, &cdef);
@@ -3395,7 +3398,8 @@ bool Type::has_done_attribute()
 }
 
 void Type::generate_code_object(const_def *cdef, Scope *p_scope,
-  const string& name, const char *prefix, bool is_template, bool has_err_descr)
+  const string& name, const char *prefix, bool is_template, bool has_err_descr,
+  bool in_class)
 {
   string type_name;
   if (is_template) type_name = get_genname_template(p_scope);
@@ -3403,23 +3407,32 @@ void Type::generate_code_object(const_def *cdef, Scope *p_scope,
   const char *name_str = name.c_str();
   const char *type_name_str = type_name.c_str();
   if (prefix) {
-    cdef->decl = mputprintf(cdef->decl, "extern const %s& %s;\n",
-      type_name_str, name_str);
-    if (split_to_slices || has_err_descr) {
-      cdef->decl = mputprintf(cdef->decl, "extern %s %s%s;\n", type_name_str, prefix, name_str);
-    }
-    cdef->def = mputprintf(cdef->def, "%s%s %s%s;\n"
-      "const %s& %s = %s%s;\n", split_to_slices || has_err_descr ? "" : "static ",
-      type_name_str, prefix, name_str, type_name_str, name_str, prefix, name_str);
+    cdef->decl = mputprintf(cdef->decl, "%sconst %s& %s;\n",
+      in_class ? "" : "extern ", type_name_str, name_str);
+    if (split_to_slices || has_err_descr || in_class) {
+      cdef->decl = mputprintf(cdef->decl, "%s%s %s%s;\n",
+        in_class ? "" : "extern ", type_name_str, prefix, name_str);
+    }
+    if (!in_class) {
+      cdef->def = mputprintf(cdef->def, "%s%s %s%s;\n"
+        "const %s& %s = %s%s;\n", split_to_slices || has_err_descr ? "" : "static ",
+        type_name_str, prefix, name_str, type_name_str, name_str, prefix, name_str);
+    }
+    else {
+      cdef->def = mputprintf(cdef->def, ", %s(%s%s)", name_str, prefix, name_str);
+    }
   } else {
-    cdef->decl = mputprintf(cdef->decl, "extern %s %s;\n",
-      type_name_str, name_str);
-    cdef->def = mputprintf(cdef->def, "%s %s;\n",
-      type_name_str, name_str);
+    cdef->decl = mputprintf(cdef->decl, "%s%s %s;\n",
+      in_class ? "" : "extern ", type_name_str, name_str);
+    if (!in_class) {
+      cdef->def = mputprintf(cdef->def, "%s %s;\n",
+        type_name_str, name_str);
+    }
   }
 }
 
-void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting)
+void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting,
+                                bool in_class)
 {
   bool is_template = FALSE;
   switch (p_setting->get_st()) {
@@ -3442,7 +3455,7 @@ void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting)
   // regenerated
   generate_code_object(cdef, p_setting->get_my_scope(),
     p_setting->get_genname_own(), p_setting->get_genname_prefix(),
-    is_template, use_runtime_2);
+    is_template, use_runtime_2, in_class);
 }
 
 void Type::generate_json_schema(JSON_Tokenizer& json, bool embedded, bool as_value)
diff --git a/compiler2/asn1/AST_asn1.cc b/compiler2/asn1/AST_asn1.cc
index 9c5b1f0a2..cae93d8ae 100644
--- a/compiler2/asn1/AST_asn1.cc
+++ b/compiler2/asn1/AST_asn1.cc
@@ -1872,7 +1872,7 @@ namespace Asn {
     left->generate_code(target);
     const_def cdef;
     Code::init_cdef(&cdef);
-    left->generate_code_object(&cdef, right);
+    left->generate_code_object(&cdef, right, false);
     cdef.init = right->generate_code_init(cdef.init,
       right->get_lhs_name().c_str());
     Code::merge_cdef(target, &cdef);
diff --git a/compiler2/asn1/Object.cc b/compiler2/asn1/Object.cc
index 117557cb4..c52a1d481 100644
--- a/compiler2/asn1/Object.cc
+++ b/compiler2/asn1/Object.cc
@@ -722,7 +722,7 @@ namespace Asn {
     if(defval) {
       const_def cdef;
       Code::init_cdef(&cdef);
-      fixtype->generate_code_object(&cdef, defval);
+      fixtype->generate_code_object(&cdef, defval, false);
       cdef.init = defval->generate_code_init(cdef.init,
         defval->get_lhs_name().c_str());
       Code::merge_cdef(target, &cdef);
@@ -1316,7 +1316,7 @@ namespace Asn {
     const_def cdef;
     Code::init_cdef(&cdef);
     Type *type = setting->get_my_governor();
-    type->generate_code_object(&cdef, setting);
+    type->generate_code_object(&cdef, setting, false);
     cdef.init = setting->generate_code_init(cdef.init,
       setting->get_lhs_name().c_str());
     Code::merge_cdef(target, &cdef);
diff --git a/compiler2/main.cc b/compiler2/main.cc
index e98ab2892..6e4254a56 100644
--- a/compiler2/main.cc
+++ b/compiler2/main.cc
@@ -100,7 +100,7 @@ boolean generate_skeleton = FALSE, force_overwrite = FALSE,
   warnings_for_bad_variants = FALSE, debugger_active = FALSE,
   legacy_unbound_union_fields = FALSE, split_to_slices = FALSE,
   legacy_untagged_union, disable_user_info, legacy_codec_handling = FALSE,
-  realtime_features = FALSE;
+  realtime_features = FALSE, oop_features = FALSE;
 
 // Default code splitting mode is set to 'no splitting'.
 CodeGenHelper::split_type code_splitting_mode = CodeGenHelper::SPLIT_NONE;
@@ -503,7 +503,7 @@ int main(int argc, char *argv[])
     Sflag = false, Kflag = false, jflag = false, zflag = false, Fflag = false,
     Mflag = false, Eflag = false, nflag = false, Bflag = false, errflag = false,
     print_usage = false, ttcn2json = false, Nflag = false, Dflag = false,
-    eflag = false, Oflag = false, Iflag = false;
+    eflag = false, Oflag = false, Iflag = false, kflag = false;
 
   CodeGenHelper cgh;
 
@@ -599,7 +599,7 @@ int main(int argc, char *argv[])
 
   if (!ttcn2json) {
     for ( ; ; ) {
-      int c = getopt(argc, argv, "aA:bBcC:dDeEfFgiIjJ:K:lLMnNo:OpP:qQ:rRsStT:uU:vV:wxXyYz:0-");
+      int c = getopt(argc, argv, "aA:bBcC:dDeEfFgiIjJ:kK:lLMnNo:OpP:qQ:rRsStT:uU:vV:wxXyYz:0-");
       if (c == -1) break;
       switch (c) {
       case 'a':
@@ -680,6 +680,10 @@ int main(int argc, char *argv[])
       case 'J':
         file_list_file_name = optarg;
         break;
+      case 'k':
+        SET_FLAG(k);
+        oop_features = TRUE;
+        break;
       case 'K':
         SET_FLAG(K);
         tcov_file_name = optarg;
@@ -846,7 +850,7 @@ int main(int argc, char *argv[])
         bflag || fflag || iflag || lflag || oflag || pflag || qflag ||
         rflag || sflag || tflag || uflag || wflag || xflag || Xflag || Rflag ||
         Uflag || yflag || Kflag || jflag || zflag || Fflag || Mflag || Eflag ||
-        nflag || Bflag || Dflag || eflag || Oflag || Iflag || s0flag) {
+        nflag || Bflag || Dflag || eflag || Oflag || Iflag || s0flag || kflag) {
         errflag = true;
         print_usage = true;
       }
diff --git a/compiler2/main.hh b/compiler2/main.hh
index fdd0c12c7..6f020af52 100644
--- a/compiler2/main.hh
+++ b/compiler2/main.hh
@@ -52,7 +52,7 @@ extern boolean generate_skeleton, force_overwrite, include_line_info,
   implicit_json_encoding, json_refs_for_all_types, force_gen_seof,
   omit_in_value_list, warnings_for_bad_variants, debugger_active,
   legacy_unbound_union_fields, split_to_slices, legacy_untagged_union,
-  disable_user_info, legacy_codec_handling, realtime_features;
+  disable_user_info, legacy_codec_handling, realtime_features, oop_features;
 
 extern const char *expected_platform;
 
diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index dac7ed53a..97477618e 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -1233,6 +1233,14 @@ namespace Ttcn {
   {
     return get_parent_scope()->get_ass_bySRef(p_ref);
   }
+  
+  bool NameBridgingScope::is_class_scope() const
+  {
+    if (parent_scope == NULL) {
+      FATAL_ERROR("NameBridgingScope::is_class_scope()");
+    }
+    return parent_scope->is_class_scope();
+  }
 
   // =================================
   // ===== RunsOnScope
@@ -2013,6 +2021,14 @@ namespace Ttcn {
   Ttcn::Definition* Definitions::get_raw_ass_byIndex(size_t p_i) {
     return ass_v[p_i];
   }
+  
+  bool Definitions::is_class_scope() const
+  {
+    if (parent_scope == NULL) {
+      FATAL_ERROR("Definitions::is_class_scope()");
+    }
+    return parent_scope->is_class_scope();
+  }
 
   void Definitions::chk_uniq()
   {
@@ -2089,7 +2105,16 @@ namespace Ttcn {
 
   void Definitions::generate_code(output_struct* target)
   {
-    for(size_t i = 0; i < ass_v.size(); i++) ass_v[i]->generate_code(target);
+    bool in_class = parent_scope->is_class_scope();
+    for (size_t i = 0; i < ass_v.size(); i++) {
+      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" :
+          "protected"));
+      }
+      ass_v[i]->generate_code(target);
+    }
   }
 
   void Definitions::generate_code(CodeGenHelper& cgh) {
@@ -3784,7 +3809,8 @@ namespace Ttcn {
     type->generate_code(target);
     const_def cdef;
     Code::init_cdef(&cdef);
-    type->generate_code_object(&cdef, value);
+    bool in_class = my_scope->is_class_scope();
+    type->generate_code_object(&cdef, value, in_class);
     if (value->is_unfoldable()) {
     cdef.post = update_location_object(cdef.post);
     cdef.post = value->generate_code_init(cdef.post,
@@ -3794,7 +3820,7 @@ namespace Ttcn {
     cdef.init = value->generate_code_init(cdef.init,
       value->get_lhs_name().c_str());
     }
-    Code::merge_cdef(target, &cdef);
+    Code::merge_cdef(target, &cdef, in_class);
     Code::free_cdef(&cdef);
   }
 
@@ -4069,7 +4095,7 @@ namespace Ttcn {
     Code::init_cdef(&cdef);
     const string& t_genname = get_genname();
     const char *name = t_genname.c_str();
-    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false);
+    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false, false);
     if (def_value) {
       cdef.init = update_location_object(cdef.init);
       cdef.init = def_value->generate_code_init(cdef.init, def_value->get_lhs_name().c_str());
@@ -4248,7 +4274,7 @@ namespace Ttcn {
     Code::init_cdef(&cdef);
     const string& t_genname = get_genname();
     const char *name = t_genname.c_str();
-    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false);
+    type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false, false);
     if (def_template) {
       cdef.init = update_location_object(cdef.init);
       cdef.init = def_template->generate_code_init(cdef.init, def_template->get_lhs_name().c_str());
@@ -4761,7 +4787,8 @@ namespace Ttcn {
       // non-parameterized template
       const_def cdef;
       Code::init_cdef(&cdef);
-      type->generate_code_object(&cdef, body);
+      bool in_class = my_scope->is_class_scope();
+      type->generate_code_object(&cdef, body, in_class);
       cdef.init = update_location_object(cdef.init);
       if (base_template) {
         // modified template
@@ -4812,12 +4839,12 @@ namespace Ttcn {
           body->get_lhs_name().c_str(), body->get_lhs_name().c_str(),
           static_cast<unsigned long>( body->get_err_descr()->get_descr_index(NULL) ));
       }
-      target->header.global_vars = mputstr(target->header.global_vars,
-        cdef.decl);
-      target->source.global_vars = mputstr(target->source.global_vars,
-        cdef.def);
-      target->functions.post_init = mputstr(target->functions.post_init,
-        cdef.init);
+      char*& header = in_class ? target->header.class_defs : target->header.global_vars;
+      header = mputstr(header, cdef.decl);
+      char*& source = in_class ? target->temp.constructor_init : target->source.global_vars;
+      source = mputstr(source, cdef.def);
+      char*& init = in_class ? target->temp.constructor : target->functions.post_init;
+      init = mputstr(init, cdef.init);
       Code::free_cdef(&cdef);
     }
   }
@@ -5076,12 +5103,15 @@ namespace Ttcn {
     type->generate_code(target);
     const_def cdef;
     Code::init_cdef(&cdef);
-    type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false);
-    Code::merge_cdef(target, &cdef);
+    bool in_class = my_scope->is_class_scope();
+    type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false,
+      in_class);
+    Code::merge_cdef(target, &cdef, in_class);
     Code::free_cdef(&cdef);
     if (initial_value) {
-      target->functions.init_comp =
-      initial_value->generate_code_init(target->functions.init_comp,
+      char*& init = in_class ? target->temp.constructor :
+        target->functions.init_comp;
+      init = initial_value->generate_code_init(init,
         initial_value->get_lhs_name().c_str());
     } else if (clean_up) {  // No initial value.
       target->functions.init_comp = mputprintf(target->functions.init_comp,
@@ -5288,22 +5318,24 @@ namespace Ttcn {
     type->generate_code(target);
     const_def cdef;
     Code::init_cdef(&cdef);
-    type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false);
-    Code::merge_cdef(target, &cdef);
+    bool in_class = my_scope->is_class_scope();
+    type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false,
+      in_class);
+    Code::merge_cdef(target, &cdef, in_class);
     Code::free_cdef(&cdef);
     if (initial_value) {
+      char*& init = in_class ? target->temp.constructor :
+        target->functions.init_comp;
       if (Common::Type::T_SEQOF == initial_value->get_my_governor()->get_typetype() ||
           Common::Type::T_ARRAY == initial_value->get_my_governor()->get_typetype()) {
-        target->functions.init_comp = mputprintf(target->functions.init_comp, 
-          "%s.remove_all_permutations();\n", initial_value->get_lhs_name().c_str());
+        init = mputprintf(init, "%s.remove_all_permutations();\n",
+          initial_value->get_lhs_name().c_str());
       }
-      target->functions.init_comp =
-        initial_value->generate_code_init(target->functions.init_comp,
+      init = initial_value->generate_code_init(init,
           initial_value->get_lhs_name().c_str());
       if (template_restriction!=TR_NONE && gen_restriction_check)
-        target->functions.init_comp = Template::generate_restriction_check_code(
-          target->functions.init_comp, initial_value->get_lhs_name().c_str(),
-          template_restriction);
+        init = Template::generate_restriction_check_code(init,
+          initial_value->get_lhs_name().c_str(), template_restriction);
     } else if (clean_up) {  // No initial value.
       // Always reset component variables/variable templates on component
       // reinitialization.  Fix for HM79493.
@@ -5649,54 +5681,82 @@ namespace Ttcn {
     const string& t_genname = get_genname();
     const char *genname_str = t_genname.c_str();
     const string& dispname = id->get_dispname();
+    bool in_class = my_scope->is_class_scope();
+    char*& header = in_class ? target->header.class_defs :
+      target->header.global_vars;
+    char*& source = in_class ? target->temp.constructor :
+      target->source.global_vars;
+    char*& pre_init = in_class ? target->temp.constructor :
+      target->functions.pre_init;
+    char*& post_init = in_class ? target->temp.constructor :
+      target->functions.post_init;
     if (dimensions) {
       // timer array
       const string& array_type = dimensions->get_timer_type();
       const char *array_type_str = array_type.c_str();
-      target->header.global_vars = mputprintf(target->header.global_vars,
-        "extern %s %s;\n", array_type_str, genname_str);
-      target->source.global_vars = mputprintf(target->source.global_vars,
-        "%s %s;\n", array_type_str, genname_str);
-      target->functions.pre_init = mputstr(target->functions.pre_init, "{\n"
+      header = mputprintf(header,
+        "%s%s %s;\n", in_class ? "" : "extern ", array_type_str, genname_str);
+      if (!in_class) {
+        source = mputprintf(source, "%s %s;\n", array_type_str, genname_str);
+      }
+      pre_init = mputstr(pre_init, "{\n"
         "static const char * const timer_name = \"");
-      target->functions.pre_init = mputstr(target->functions.pre_init,
+      pre_init = mputstr(pre_init,
         dispname.c_str());
-      target->functions.pre_init = mputprintf(target->functions.pre_init,
-        "\";\n"
+      pre_init = mputprintf(pre_init, "\";\n"
         "%s.set_name(timer_name);\n"
         "}\n", genname_str);
-      if (default_duration) target->functions.post_init =
-        generate_code_array_duration(target->functions.post_init, genname_str,
+      if (default_duration) post_init =
+        generate_code_array_duration(post_init, genname_str,
           default_duration);
     } else {
       // single timer
-      target->header.global_vars = mputprintf(target->header.global_vars,
-        "extern TIMER %s;\n", genname_str);
+      header = mputprintf(header, "%sTIMER %s;\n",
+        in_class ? "" : "extern ", genname_str);
       if (default_duration) {
         // has default duration
         Value *v = default_duration->get_value_refd_last();
         if (v->get_valuetype() == Value::V_REAL) {
           // duration is known at compilation time -> set in the constructor
-          target->source.global_vars = mputprintf(target->source.global_vars,
-            "TIMER %s(\"%s\", %s);\n", genname_str, dispname.c_str(),
-            v->get_single_expr().c_str());
+          if (in_class) {
+            source = mputprintf(source,
+              "%s.set_name(\"%s\");\n"
+              "%s.set_default_duration(%s);\n",
+              genname_str, dispname.c_str(),
+              genname_str, v->get_single_expr().c_str());
+          }
+          else {
+            source = mputprintf(source, "TIMER %s(\"%s\", %s);\n",
+              genname_str, dispname.c_str(), v->get_single_expr().c_str());
+          }
         } else {
           // duration is known only at runtime -> set in post_init
-          target->source.global_vars = mputprintf(target->source.global_vars,
-            "TIMER %s(\"%s\");\n", genname_str, dispname.c_str());
+          if (in_class) {
+            source = mputprintf(source, "%s.set_name(\"%s\");\n",
+              genname_str, dispname.c_str());
+          }
+          else {
+            source = mputprintf(source, "TIMER %s(\"%s\");\n",
+              genname_str, dispname.c_str());
+          }
           expression_struct expr;
           Code::init_expr(&expr);
           expr.expr = mputprintf(expr.expr, "%s.set_default_duration(",
             genname_str);
           default_duration->generate_code_expr(&expr);
           expr.expr = mputc(expr.expr, ')');
-          target->functions.post_init =
-            Code::merge_free_expr(target->functions.post_init, &expr);
+          post_init = Code::merge_free_expr(post_init, &expr);
         }
       } else {
         // does not have default duration
-        target->source.global_vars = mputprintf(target->source.global_vars,
-          "TIMER %s(\"%s\");\n", genname_str, dispname.c_str());
+        if (in_class) {
+          source = mputprintf(source, "%s.set_name(\"%s\");\n",
+            genname_str, dispname.c_str());
+        }
+        else {
+          source = mputprintf(source, "TIMER %s(\"%s\");\n",
+            genname_str, dispname.c_str());
+        }
       }
     }
   }
@@ -6669,27 +6729,34 @@ namespace Ttcn {
     // smart formal parameter list (names of unused parameters are omitted)
     char *formal_par_list = fp_list->generate_code(memptystr());
     fp_list->generate_code_defval(target);
+    
+    bool in_class = my_scope->is_class_scope();
+    char*& header = in_class ? target->header.class_defs :
+      target->header.function_prototypes;
+    char*& source = in_class ? target->source.methods :
+      target->source.function_bodies;
+    
     // function prototype
-    target->header.function_prototypes =
-      mputprintf(target->header.function_prototypes, "%s%s %s(%s);\n",
-        get_PortType() && clean_up ? "" : "extern ",
-        return_type_str, genname_str, formal_par_list);
+    header = mputprintf(header, "%s%s %s(%s);\n",
+      get_PortType() && clean_up ? "" : (in_class ? "virtual " : "extern "),
+      return_type_str, genname_str, formal_par_list);
 
     // function body    
-    target->source.function_bodies = mputprintf(target->source.function_bodies,
+    source = mputprintf(source,
       "%s %s%s%s%s(%s)\n"
       "{\n"
       "%s"
       "}\n\n",
       return_type_str,
-      port_type && clean_up ? port_type->get_genname_own().c_str() : "",
+      port_type && clean_up ? port_type->get_genname_own().c_str() :
+      (in_class ? my_scope->get_scope_class()->get_id()->get_name().c_str() : ""),
       port_type && clean_up && port_type->get_PortBody()->get_testport_type() != PortTypeBody::TP_INTERNAL ? "_BASE" : "",
-      port_type && clean_up ? "::" : "",
+      in_class || (port_type && clean_up) ? "::" : "",
       genname_str, formal_par_list, body);
     Free(formal_par_list);
     Free(body);
 
-    if (is_startable) {
+    if (is_startable && !in_class) {
       size_t nof_fps = fp_list->get_nof_fps();
       // use the full list of formal parameters here (since they are all logged)
       char *full_formal_par_list = fp_list->generate_code(memptystr(), nof_fps);
@@ -6800,15 +6867,17 @@ namespace Ttcn {
       Free(full_formal_par_list);
     }
 
-    target->functions.pre_init = mputprintf(target->functions.pre_init,
-      "%s.add_function(\"%s\", (genericfunc_t)&%s, ", get_module_object_name(),
-      dispname_str, genname_str);
-    if(is_startable)
+    if (!in_class) {
       target->functions.pre_init = mputprintf(target->functions.pre_init,
-        "(genericfunc_t)&start_%s);\n", genname_str);
-    else
-      target->functions.pre_init = mputstr(target->functions.pre_init,
-        "NULL);\n");
+        "%s.add_function(\"%s\", (genericfunc_t)&%s, ", get_module_object_name(),
+        dispname_str, genname_str);
+      if(is_startable)
+        target->functions.pre_init = mputprintf(target->functions.pre_init,
+          "(genericfunc_t)&start_%s);\n", genname_str);
+      else
+        target->functions.pre_init = mputstr(target->functions.pre_init,
+          "NULL);\n");
+    }
   }
 
   void Def_Function::generate_code(CodeGenHelper& cgh) {
@@ -7556,10 +7625,13 @@ namespace Ttcn {
     const char *return_type_str = return_type_name.c_str();
     char *formal_par_list = fp_list->generate_code(memptystr(), fp_list->get_nof_fps());
     fp_list->generate_code_defval(target);
+    
+    bool in_class = my_scope->is_class_scope();
     // function prototype
-    target->header.function_prototypes =
-      mputprintf(target->header.function_prototypes, "extern %s %s(%s);\n",
-        return_type_str, genname_str, formal_par_list);
+    char*& header = in_class ? target->header.class_defs :
+      target->header.function_prototypes;
+    header = mputprintf(header, "%s %s %s(%s);\n",
+      in_class ? "virtual" : "extern", return_type_str, genname_str, formal_par_list);
 
     if (function_type != EXTFUNC_MANUAL) {
       // function body written by the compiler
@@ -7593,9 +7665,11 @@ namespace Ttcn {
 
     Free(formal_par_list);
 
-    target->functions.pre_init = mputprintf(target->functions.pre_init,
-      "%s.add_function(\"%s\", (genericfunc_t)&%s, NULL);\n",
-      get_module_object_name(), id->get_dispname().c_str(), genname_str);
+    if (!in_class) {
+      target->functions.pre_init = mputprintf(target->functions.pre_init,
+        "%s.add_function(\"%s\", (genericfunc_t)&%s, NULL);\n",
+        get_module_object_name(), id->get_dispname().c_str(), genname_str);
+    }
   }
 
   void Def_ExtFunction::generate_code(CodeGenHelper& cgh) {
@@ -7740,6 +7814,56 @@ namespace Ttcn {
       json->put_next_token(JSON_TOKEN_OBJECT_END);
     }
   }
+  
+  // =================================
+  // ===== Def_AbsFunction
+  // =================================
+  
+  Def_AbsFunction::~Def_AbsFunction()
+  {
+    // TODO
+  }
+  
+  Definition* Def_AbsFunction::clone() const
+  {
+    return NULL;
+    // TODO
+  }
+  
+  void Def_AbsFunction::chk()
+  {
+    // TODO
+  }
+  
+  void Def_AbsFunction::generate_code(output_struct* target, bool)
+  {
+    const string& t_genname = get_genname();
+    const char* genname_str = t_genname.c_str();
+    string return_type_name;
+    switch (asstype) {
+    case A_FUNCTION:
+      return_type_name = "void";
+      break;
+    case A_FUNCTION_RVAL:
+      return_type_name = return_type->get_genname_value(my_scope);
+      break;
+    case A_FUNCTION_RTEMP:
+      return_type_name = return_type->get_genname_template(my_scope);
+      break;
+    default:
+      FATAL_ERROR("Def_AbsFunction::generate_code");
+    }
+    const char* return_type_str = return_type_name.c_str();
+    char* formal_par_list = fp_list->generate_code(memptystr(), fp_list->get_nof_fps());
+    fp_list->generate_code_defval(target);
+    
+    target->header.class_defs = mputprintf(target->header.class_defs,
+      "virtual %s %s(%s) = 0;\n", return_type_str, genname_str, formal_par_list);
+    
+    Free(formal_par_list);
+  }
+  
+  // TODO
 
   // =================================
   // ===== Def_Altstep
@@ -8352,6 +8476,78 @@ namespace Ttcn {
     if (block)
       block->set_parent_path(w_attrib_path);
   }
+  
+  // =================================
+  // ===== Def_Constructor
+  // =================================
+  
+  Def_Constructor::Def_Constructor(FormalParList* p_fp_list,
+    Ref_pard* p_base_call, StatementBlock* p_block)
+  : Definition(A_CONSTRUCTOR, new Common::Identifier(
+    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) {
+      FATAL_ERROR("Def_Constructor::Def_Constructor");
+    }
+  }
+  
+  Def_Constructor::~Def_Constructor()
+  {
+    delete fp_list;
+    delete base_call;
+    delete block;
+  }
+  
+  Def_Constructor *Def_Constructor::clone() const
+  {
+    FATAL_ERROR("Def_Constructor::clone");
+  }
+
+  void Def_Constructor::set_fullname(const string& p_fullname)
+  {
+    Definition::set_fullname(p_fullname);
+    fp_list->set_fullname(p_fullname + ".<formal_par_list>");
+    if (base_call != NULL) {
+      base_call->set_fullname(p_fullname + ".<base_call>");
+    }
+    block->set_fullname(p_fullname + ".<statement_block>");
+  }
+
+  void Def_Constructor::set_my_scope(Scope* p_scope)
+  {
+    //bridgeScope.set_parent_scope(p_scope);
+    //bridgeScope.set_scopeMacro_name(id->get_dispname());
+
+    //Definition::set_my_scope(&bridgeScope);
+    Definition::set_my_scope(p_scope); // TODO
+    if (base_call != NULL) {
+      base_call->set_my_scope(p_scope);
+    }
+    block->set_my_scope(fp_list);
+  }
+
+  FormalParList* Def_Constructor::get_FormalParList()
+  {
+    if (!checked) chk();
+    return fp_list;
+  }
+  
+  void Def_Constructor::chk()
+  {
+    // TODO
+  }
+  
+  void Def_Constructor::generate_code(output_struct *target, bool clean_up)
+  {
+    // TODO
+  }
+  
+  void Def_Constructor::set_parent_path(WithAttribPath* p_path)
+  {
+    Definition::set_parent_path(p_path);
+    block->set_parent_path(w_attrib_path);
+  }
 
   // =================================
   // ===== FormalPar
@@ -9210,7 +9406,7 @@ namespace Ttcn {
       Value *val = defval.ap->get_Value();
       const_def cdef;
       Code::init_cdef(&cdef);
-      type->generate_code_object(&cdef, val);
+      type->generate_code_object(&cdef, val, false);
       Code::merge_cdef(target, &cdef);
       Code::free_cdef(&cdef);
       break; }
@@ -9222,7 +9418,7 @@ namespace Ttcn {
       Template *temp = ti->get_Template();
       const_def cdef;
       Code::init_cdef(&cdef);
-      type->generate_code_object(&cdef, temp);
+      type->generate_code_object(&cdef, temp, false);
       Code::merge_cdef(target, &cdef);
       Code::free_cdef(&cdef);
       break; }
diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh
index 79bc745a1..fa1eb6cd2 100644
--- a/compiler2/ttcn3/AST_ttcn3.hh
+++ b/compiler2/ttcn3/AST_ttcn3.hh
@@ -428,6 +428,7 @@ namespace Ttcn {
     virtual string get_scopeMacro_name() const;
     virtual NameBridgingScope* clone() const;
     virtual Common::Assignment* get_ass_bySRef(Ref_simple *p_ref);
+    virtual bool is_class_scope() const;
   };
 
   /**
@@ -524,6 +525,7 @@ namespace Ttcn {
     virtual Common::Assignment* get_ass_byIndex(size_t p_i);
     size_t get_nof_raw_asss();
     Definition *get_raw_ass_byIndex(size_t p_i);
+    virtual bool is_class_scope() const;
     /** Checks the uniqueness of identifiers. */
     void chk_uniq();
     /** Checks all definitions. */
@@ -1601,6 +1603,24 @@ namespace Ttcn {
       * inserted in the schema. */
     void generate_json_schema_ref(map<Type*, JSON_Tokenizer>& json_refs);
   };
+  
+  /**
+    * Represents an abstract function definition (inside a class definition)
+    */
+  class Def_AbsFunction : public Def_Function_Base {
+    bool deterministic;
+  public:
+    Def_AbsFunction(bool p_deterministic, Identifier* p_id, FormalParList* p_fpl,
+      Type* p_return_type, bool returns_template,
+      template_restriction_t p_template_restriction)
+    : Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template,
+      p_template_restriction) { }
+    virtual ~Def_AbsFunction();
+    virtual Definition* clone() const;
+    virtual void chk();
+    virtual void generate_code(output_struct* target, bool clean_up = false);
+    // TODO
+  };
 
   /**
    * Represents an altstep definition.
@@ -1717,6 +1737,38 @@ namespace Ttcn {
 
     virtual void set_parent_path(WithAttribPath* p_path);
   };
+  
+  /**
+   * Represents a constructor definition (inside a class definition).
+   */
+  class Def_Constructor : public Definition {
+  private:
+    /** The formal parameter list of the constructor. It is never NULL even if
+     * the constructor has no parameters. */
+    FormalParList* fp_list;
+    
+    Ref_pard* base_call;
+    
+    StatementBlock* block;
+    
+    //NameBridgingScope bridgeScope;
+    
+    /// Copy constructor disabled
+    Def_Constructor(const Def_Constructor& p);
+    /// %Assignment disabled
+    Def_Constructor& operator=(const Def_Constructor& p);
+  public:
+    Def_Constructor(FormalParList* p_fp_list, Ref_pard* p_base_call,
+      StatementBlock* p_block);
+    virtual ~Def_Constructor();
+    virtual Def_Constructor* clone() const;
+    virtual void set_fullname(const string& p_fullname);
+    virtual void set_my_scope(Scope* p_scope);
+    virtual FormalParList *get_FormalParList();
+    virtual void chk();
+    virtual void generate_code(output_struct *target, bool clean_up = false);
+    virtual void set_parent_path(WithAttribPath* p_path);
+  };
 
   /** General class to represent formal parameters. The inherited
    * attribute asstype carries the kind and direction of the
diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc
index 9eab7fdcb..b46e06a04 100644
--- a/compiler2/ttcn3/Ttcnstuff.cc
+++ b/compiler2/ttcn3/Ttcnstuff.cc
@@ -23,6 +23,7 @@
 #include "../main.hh"
 #include "Attributes.hh"
 #include <errno.h>
+#include "Statement.hh"
 
 // implemented in coding_attrib_p.y
 extern Ttcn::ExtensionAttributes * parse_extattributes(
@@ -379,17 +380,17 @@ namespace Ttcn {
   void TypeMappingTarget::chk_function(Type *source_type, Type *port_type, bool legacy, bool incoming)
   {
     Error_Context cntxt(this, "In `function' mapping");
-    Assignment *t_ass = u.func.function_ref->get_refd_assignment(false);
+    Common::Assignment *t_ass = u.func.function_ref->get_refd_assignment(false);
     if (!t_ass) return;
     t_ass->chk();
     switch (t_ass->get_asstype()) {
-    case Assignment::A_FUNCTION:
-    case Assignment::A_FUNCTION_RVAL:
-    case Assignment::A_FUNCTION_RTEMP:
+    case Common::Assignment::A_FUNCTION:
+    case Common::Assignment::A_FUNCTION_RVAL:
+    case Common::Assignment::A_FUNCTION_RTEMP:
       break;
-    case Assignment::A_EXT_FUNCTION:
-    case Assignment::A_EXT_FUNCTION_RVAL:
-    case Assignment::A_EXT_FUNCTION_RTEMP:
+    case Common::Assignment::A_EXT_FUNCTION:
+    case Common::Assignment::A_EXT_FUNCTION_RVAL:
+    case Common::Assignment::A_EXT_FUNCTION_RTEMP:
       // External functions are not allowed when the standard like behaviour is used
       if (legacy) {
         break;
@@ -1504,9 +1505,9 @@ namespace Ttcn {
     size_t n_prov_t = 0;
     for (size_t p = 0; p < provider_refs.size(); p++) {
       provider_body = 0;
-      Assignment *t_ass = provider_refs[p]->get_refd_assignment(); // provider port
+      Common::Assignment *t_ass = provider_refs[p]->get_refd_assignment(); // provider port
       if (t_ass) {
-        if (t_ass->get_asstype() == Assignment::A_TYPE) {
+        if (t_ass->get_asstype() == Common::Assignment::A_TYPE) {
           Type *t = t_ass->get_Type()->get_type_refd_last();
           if (t->get_typetype() == Type::T_PORT) {
             bool found = false;
@@ -2440,11 +2441,11 @@ namespace Ttcn {
           Definition* def = static_cast<Definition*>(vardefs->get_ass_byIndex(i));
           string type;
           switch (def->get_asstype()) {
-            case Assignment::A_VAR:
-            case Assignment::A_CONST:
+            case Common::Assignment::A_VAR:
+            case Common::Assignment::A_CONST:
               type = def->get_Type()->get_genname_value(my_scope);
               break;
-            case Assignment::A_VAR_TEMPLATE:
+            case Common::Assignment::A_VAR_TEMPLATE:
               type = def->get_Type()->get_genname_template(my_scope);
               break;
             default:
@@ -2462,7 +2463,7 @@ namespace Ttcn {
           
           // If the def does not have default value then clean it up to
           // restore to unbound. (for constants the generate_code_init_comp does nothing)
-          if ((pdef.var_defs == NULL || len == strlen(pdef.var_defs)) && def->get_asstype() != Assignment::A_CONST) {
+          if ((pdef.var_defs == NULL || len == strlen(pdef.var_defs)) && def->get_asstype() != Common::Assignment::A_CONST) {
             pdef.var_defs = mputprintf(pdef.var_defs, "%s.clean_up();\n",
               def->get_genname().c_str());
           }
@@ -2973,5 +2974,309 @@ namespace Ttcn {
     }
     other->clear();
   }
+  
+  // =================================
+  // ===== ClassTypeBody
+  // =================================
+  
+  ClassTypeBody::ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final,
+                               boolean p_abstract, Common::Type* p_base_type, Reference* p_runs_on_ref,
+                               Reference* p_mtc_ref, Reference* p_system_ref,
+                               Definitions* p_members, StatementBlock* p_finally_block)
+  : Scope(), Location(), class_id(p_class_id), external(p_external), final(p_final), abstract(p_abstract),
+    base_type(p_base_type), runs_on_ref(p_runs_on_ref), mtc_ref(p_mtc_ref), system_ref(p_system_ref),
+    members(p_members), finally_block(p_finally_block), checked(false)
+  {
+    if (members == NULL) {
+      FATAL_ERROR("ClassTypeBody::ClassTypeBody");
+    }
+  }
+  
+  ClassTypeBody::ClassTypeBody(const ClassTypeBody& p)
+  {
+    class_id = p.class_id->clone();
+    external = p.external;
+    final = p.final;
+    abstract = p.abstract;
+    base_type = p.base_type != NULL ? p.base_type->clone() : NULL;
+    runs_on_ref = p.runs_on_ref != NULL ? p.runs_on_ref->clone() : NULL;
+    mtc_ref = p.mtc_ref != NULL ? p.mtc_ref->clone() : NULL;
+    system_ref = p.system_ref != NULL ? p.system_ref->clone() : NULL;
+    members = p.members->clone();
+    finally_block = p.finally_block != NULL ? p.finally_block->clone() : NULL;
+    checked = p.checked;
+  }
+  
+  ClassTypeBody* ClassTypeBody::clone() const
+  {
+    return new ClassTypeBody(*this);
+  }
+  
+  ClassTypeBody::~ClassTypeBody()
+  {
+    delete base_type;
+    delete finally_block;
+    delete members;
+    delete mtc_ref;
+    delete runs_on_ref;
+    delete system_ref;
+  }
+  
+  void ClassTypeBody::set_fullname(const string& p_fullname)
+  {
+    Common::Scope::set_fullname(p_fullname);
+    if (base_type != NULL) {
+      base_type->set_fullname(p_fullname + ".<base_type>");
+    }
+    if (runs_on_ref != NULL) {
+      runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>");
+    }
+    if (mtc_ref != NULL) {
+      mtc_ref->set_fullname(p_fullname + ".<mtc_type>");
+    }
+    if (system_ref != NULL) {
+      system_ref->set_fullname(p_fullname + ".<system_type>");
+    }
+    members->set_fullname(p_fullname + ".<members>");
+    if (finally_block != NULL) {
+      finally_block->set_fullname(p_fullname + ".<finally_block>");
+    }
+  }
+  
+  void ClassTypeBody::set_my_scope(Scope* p_scope)
+  {
+    set_parent_scope(p_scope);
+    if (base_type != NULL) {
+      base_type->set_my_scope(p_scope);
+    }
+    if (runs_on_ref != NULL) {
+      runs_on_ref->set_my_scope(p_scope);
+    }
+    if (mtc_ref != NULL) {
+      mtc_ref->set_my_scope(p_scope);
+    }
+    if (system_ref != NULL) {
+      system_ref->set_my_scope(p_scope);
+    }
+    //members->set_my_scope(p_scope);
+    /*if (finally_block != NULL) {
+      finally_block->set_my_scope(p_scope);
+    }*/
+  }
+  
+  void ClassTypeBody::dump(unsigned level) const
+  {
+    DEBUG(level, "Modifiers:%s%s%s", external ? "external " : "",
+      final ? " @final " : "", abstract ? " @abstract" : "");
+    DEBUG(level, "Base class: %s", base_type != NULL ?
+      base_type->get_typename().c_str() : "");
+    if (runs_on_ref != NULL) {
+      DEBUG(level, "Runs on clause:");
+      runs_on_ref->dump(level + 1);
+    }
+    if (mtc_ref != NULL) {
+      DEBUG(level, "MTC clause:");
+      mtc_ref->dump(level + 1);
+    }
+    if (system_ref != NULL) {
+      DEBUG(level, "System clause:");
+      system_ref->dump(level + 1);
+    }
+    DEBUG(level, "Members:");
+    members->dump(level + 1);
+    DEBUG(level, "%s `finally' block", finally_block != NULL ?
+      "Has" : "Doesn't have");
+  }
+  
+  bool ClassTypeBody::is_parent_class(const ClassTypeBody* p_class) const
+  {
+    if (this == p_class) {
+      return true;
+    }
+    if (base_type == NULL) {
+      return false;
+    }
+    return base_type->get_type_refd_last()->get_class_type_body()->
+      is_parent_class(p_class);
+  }
+  
+  bool ClassTypeBody::has_local_ass_withId(const Identifier& p_id)
+  {
+    chk();
+    if (members->has_local_ass_withId(p_id)) {
+      return true;
+    }
+    if (base_type != NULL) {
+      return base_type->get_type_refd_last()->get_class_type_body()->
+        has_local_ass_withId(p_id);
+    }
+    else {
+      return false;
+    }
+  }
+  
+  Common::Assignment* ClassTypeBody::get_local_ass_byId(const Identifier& p_id)
+  {
+    chk();
+    Common::Assignment* ass = NULL;
+    if (members->has_local_ass_withId(p_id)) {
+      ass = members->get_local_ass_byId(p_id);
+    }
+    if (ass == NULL && base_type != NULL) {
+      ass = base_type->get_type_refd_last()->get_class_type_body()->
+        get_local_ass_byId(p_id);
+    }
+    return ass;
+  }
+  
+  Common::Assignment* ClassTypeBody::get_ass_bySRef(Common::Ref_simple* p_ref)
+  {
+    if (p_ref == NULL || parent_scope == NULL) {
+      FATAL_ERROR("ClassTypeBody::get_ass_bySRef()");
+    }
+    if (p_ref->get_modid() == NULL) {
+      const Common::Identifier* id = p_ref->get_id();
+      if (id != NULL && has_local_ass_withId(*id)) {
+        Common::Assignment* ass = get_local_ass_byId(*id);
+        if (ass == NULL) {
+          FATAL_ERROR("ClassTypeBody::get_ass_bySRef()");
+        }
+
+        if (ass->get_visibility() == PUBLIC) {
+          // it's public, so it doesn't matter where it's accessed from
+          return ass;
+        }
+        
+        const ClassTypeBody* ref_scope_class = p_ref->get_my_scope()->get_scope_class();
+        if (ref_scope_class == this) {
+          // the reference is inside this class => any visibility is fine
+          return ass;
+        }
+        
+        if (ref_scope_class != NULL &&
+            ass->get_visibility() == NOCHANGE && // i.e. protected
+            ref_scope_class->is_parent_class(this)) {
+          return ass;
+        }
+
+        p_ref->error("The member definition `%s' in class type `%s'"
+          " is not visible in this scope", id->get_dispname().c_str(),
+            class_id->get_dispname().c_str());
+        return NULL;
+      }
+    }
+    return parent_scope->get_ass_bySRef(p_ref);
+  }
+  
+  void ClassTypeBody::chk()
+  {
+    if (checked) {
+      return;
+    }
+    checked = true;
+    // TODO: external? final? abstract?
+    if (base_type != NULL) {
+      base_type->chk();
+      // TODO: additional checks for the base type
+    }
+
+    if (runs_on_ref != NULL) {
+      Common::Assignment* ass = runs_on_ref->get_refd_assignment(true);
+      // TODO
+    }
+    if (mtc_ref != NULL) {
+      Common::Assignment* ass = mtc_ref->get_refd_assignment(true);
+      // TODO
+    }
+    if (system_ref != NULL) {
+      Common::Assignment* ass = system_ref->get_refd_assignment(true);
+      // TODO
+    }
+
+    members->set_parent_scope(this);
+    members->chk_uniq();
+    members->chk();
+
+    if (finally_block != NULL) {
+      finally_block->set_parent_scope(this);
+      finally_block->chk();
+    }
+  }
+  
+  void ClassTypeBody::generate_code(output_struct* target)
+  {
+    target->header.class_decls = mputprintf(target->header.class_decls,
+      "class %s;\n", class_id->get_name().c_str());
+    if (!external) {
+      string base_type_name = base_type != NULL ?
+        base_type->get_genname_value(this) : string("OBJECT");
+      
+      target->header.class_defs = mputprintf(target->header.class_defs,
+        "class %s : public %s {\n",
+        class_id->get_name().c_str(), base_type_name.c_str());
+      // members
+      members->generate_code(target);
+      
+      // constructor
+      target->header.class_defs = mputprintf(target->header.class_defs,
+        "public:\n"
+        "%s();\n", class_id->get_name().c_str());
+      target->source.methods = mputprintf(target->source.methods,
+        "%s::%s()\n"
+        ": %s()",
+        class_id->get_name().c_str(), class_id->get_name().c_str(),
+        base_type_name.c_str());
+      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 != 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);
+        Free(target->temp.constructor);
+        target->temp.constructor = NULL;
+      }
+      target->source.methods = mputstr(target->source.methods, "}\n\n");
+
+      // destructor
+      target->header.class_defs = mputprintf(target->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,
+          "%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);
+        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,
+          "} catch (...) {\n"
+          "fprintf(stderr, \"Unhandled exception or dynamic test case error in "
+          "destructor of class `%s'\");\n"
+          "exit(EXIT_FAILURE);\n"
+          "}\n"
+          "}\n\n", class_id->get_name().c_str());
+      }
+
+      target->header.class_defs = mputstr(target->header.class_defs, "};\n\n");
+    }
+    else { // external class
+      target->header.class_defs = mputprintf(target->header.class_defs,
+        "#include \"%s.hh\"\n\n",
+        duplicate_underscores ? class_id->get_name().c_str() :
+          class_id->get_dispname().c_str());
+    }
+  }
 
 } // namespace Ttcn
diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh
index 42b347a5a..f036abebf 100644
--- a/compiler2/ttcn3/Ttcnstuff.hh
+++ b/compiler2/ttcn3/Ttcnstuff.hh
@@ -753,6 +753,47 @@ public:
   size_t size() const { return vector<ExtensionAttribute>::size(); }
 };
 
+
+class ClassTypeBody : public Common::Scope, public Common::Location {
+  Common::Identifier* class_id; // not owned
+  boolean external;
+  boolean final;
+  boolean abstract;
+  Common::Type* base_type;
+  Reference* runs_on_ref;
+  Reference* mtc_ref;
+  Reference* system_ref;
+  Definitions* members;
+  StatementBlock* finally_block;
+  bool checked;
+  
+public:
+  ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final,
+    boolean p_abstract, Common::Type* p_base_type, Ttcn::Reference* p_runs_on_ref,
+    Reference* p_mtc_ref, Reference* p_system_ref,
+    Definitions* p_members, StatementBlock* p_finally_block);
+  ClassTypeBody(const ClassTypeBody& p);
+  ClassTypeBody* clone() const;
+  virtual ~ClassTypeBody();
+  
+  void set_fullname(const string& p_fullname);
+  void set_my_scope(Scope* p_scope);
+  void dump(unsigned level) const;
+  
+  virtual bool is_class_scope() const { return true; }
+  virtual const ClassTypeBody* get_scope_class() const { return this; }
+  Common::Identifier* get_id() const { return class_id; }
+  
+  bool is_parent_class(const ClassTypeBody* p_class) const;
+  bool has_local_ass_withId(const Identifier& p_id);
+  Common::Assignment* get_local_ass_byId(const Identifier& p_id);
+  Common::Assignment* get_ass_bySRef(Common::Ref_simple* p_ref);
+  
+  void chk();
+  
+  void generate_code(output_struct* target);
+};
+
 }
 
 #endif /* TTCNSTUFF_H_ */
diff --git a/compiler2/ttcn3/compiler.h b/compiler2/ttcn3/compiler.h
index 2b0001605..8564d87cf 100644
--- a/compiler2/ttcn3/compiler.h
+++ b/compiler2/ttcn3/compiler.h
@@ -67,6 +67,10 @@ extern "C" {
       char *start;     /**< Code for start_ptc_function() */
       char *control;   /**< Code for module_control_part() */
     } functions;
+    struct {
+      char* constructor_init;
+      char* constructor;
+    } temp;
     struct {
       size_t pre_things_size; // Size of string_literals + global_vars
       size_t *methods;
diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l
index 1d0ffef30..a70536cb4 100644
--- a/compiler2/ttcn3/compiler.l
+++ b/compiler2/ttcn3/compiler.l
@@ -558,6 +558,30 @@ timestamp {
     RETURN_LVAL(IDentifier);
   }
 }
+class {
+  if (oop_features) {
+    RETURN(ClassKeyword);
+  }
+  else {
+    Location loc(infile, yylloc);
+    loc.warning("Keyword 'class' is treated as an identifier. Activate "
+      "compiler option '-k' to use object-oriented features.");
+    yylval.id = new Identifier(Identifier::ID_TTCN, string(yyleng, yytext));
+    RETURN_LVAL(IDentifier);
+  }
+}
+finally {
+  if (oop_features) {
+    RETURN(FinallyKeyword);
+  }
+  else {
+    Location loc(infile, yylloc);
+    loc.warning("Keyword 'finally' is treated as an identifier. Activate "
+      "compiler option '-k' to use object-oriented features.");
+    yylval.id = new Identifier(Identifier::ID_TTCN, string(yyleng, yytext));
+    RETURN_LVAL(IDentifier);
+  }
+}
 
   /* modifier keywords */
 
@@ -568,6 +592,8 @@ timestamp {
 "@fuzzy"         RETURN(FuzzyKeyword);
 "@index"         RETURN(IndexKeyword);
 "@local"         RETURN(LocalKeyword);
+"@final"         RETURN(FinalKeyword);
+"@abstract"      RETURN(AbstractKeyword);
 
   /* special TITAN specific keywords */
 
diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y
index fd0af25e9..79b1002a9 100644
--- a/compiler2/ttcn3/compiler.y
+++ b/compiler2/ttcn3/compiler.y
@@ -659,6 +659,7 @@ static const string anyname("anytype");
 %token CharStringKeyword
 %token CheckOpKeyword
 %token CheckStateKeyword
+%token ClassKeyword
 %token ClearOpKeyword
 %token ComplementKeyword
 %token ComponentKeyword
@@ -687,6 +688,7 @@ static const string anyname("anytype");
 %token ExtKeyword
 %token FailKeyword
 %token FalseKeyword
+%token FinallyKeyword
 %token FloatKeyword
 %token ForKeyword
 %token FriendKeyword
@@ -807,6 +809,8 @@ static const string anyname("anytype");
 %token FuzzyKeyword
 %token IndexKeyword
 %token LocalKeyword
+%token FinalKeyword
+%token AbstractKeyword
 
 /* TITAN specific keywords */
 %token TitanSpecificTryKeyword
@@ -942,6 +946,7 @@ static const string anyname("anytype");
 
 %type <bool_val> optAliveKeyword optOptionalKeyword
   optErrValueRaw optAllKeyword optDeterministicModifier optRealtimeClause
+  optExtKeyword optFinalModifier optAbstractModifier
 %type <str> FreeText optLanguageSpec PatternChunk PatternChunkList
 %type <uchar_val> Group Plane Row Cell
 %type <id> FieldIdentifier FieldReference GlobalModuleId
@@ -968,9 +973,11 @@ static const string anyname("anytype");
 %type <compfield> StructFieldDef UnionFieldDef
 %type <compfieldmap> StructFieldDefList optStructFieldDefList UnionFieldDefList
 %type <definition> AltstepDef ExtFunctionDef FunctionDef TemplateDef TestcaseDef
+  ClassFunctionDef ConstructorDef
+%type <defs> optClassMemberList ClassMemberList
 %type <deftype> TypeDefBody StructuredTypeDef SubTypeDef RecordDef UnionDef
   SetDef RecordOfDef SetOfDef EnumDef PortDef PortDefBody ComponentDef
-  TypeDef SignatureDef FunctionTypeDef AltstepTypeDef TestcaseTypeDef
+  TypeDef SignatureDef FunctionTypeDef AltstepTypeDef TestcaseTypeDef ClassDef
 %type <deftimer> SingleTimerInstance
 %type <enumitem> Enumeration
 %type <enumitems> EnumerationList
@@ -1028,7 +1035,7 @@ static const string anyname("anytype");
   SelectUnionConstruct UpdateStatement SetstateStatement SetencodeStatement
   StopTestcaseStatement String2TtcnStatement ProfilerStatement int2enumStatement
 %type <statementblock> StatementBlock optElseClause FunctionStatementOrDefList
-  ControlStatementOrDefList ModuleControlBody
+  ControlStatementOrDefList ModuleControlBody optFinallyDef
 %type <subtypeparse> ValueOrRange
 %type <templ> MatchingSymbol SingleValueOrAttrib SimpleSpec TemplateBody
   ArrayElementSpec ArrayValueOrAttrib FieldSpecList ArraySpecList
@@ -1046,7 +1053,7 @@ static const string anyname("anytype");
   ArrayElementSpecList SubsetMatch SupersetMatch PermutationMatch
 %type <ass> Assignment Step
 %type <refbase> DerivedRefWithParList TemplateRefWithParList DecValueArg
-%type <refpard> FunctionInstance AltstepInstance
+%type <refpard> FunctionInstance AltstepInstance optBaseConstructorCall
 %type <reference> PortType optDerivedDef DerivedDef IndexSpec Signature
   VariableRef TimerRef Port PortOrAll ValueStoreSpec
   SenderSpec ComponentType optRunsOnSpec RunsOnSpec optSystemSpec optPortSpec
@@ -1056,7 +1063,7 @@ static const string anyname("anytype");
 %type <type> NestedEnumDef NestedRecordDef NestedRecordOfDef NestedSetDef
   NestedSetOfDef NestedTypeDef NestedUnionDef PortDefAttribs ReferencedType
   Type TypeOrNestedTypeDef NestedFunctionTypeDef NestedAltstepTypeDef
-  NestedTestcaseTypeDef
+  NestedTestcaseTypeDef optExtendsClassDef
 %type <types_with_mapping> TypeList AllOrTypeList AllOrTypeListWithFrom
 AllOrTypeListWithTo TypeListWithFrom TypeListWithTo
 %type <value> AddressValue AliveOp AllPortsSpec AltGuardChar ArrayBounds
@@ -1081,7 +1088,7 @@ AllOrTypeListWithTo TypeListWithFrom TypeListWithTo
 %type <def_list> AltstepLocalDef AltstepLocalDefList ComponentElementDef
   ConstDef ExtConstDef FunctionLocalDef FunctionLocalInst ModuleDef ModulePar
   ModuleParDef MultiTypedModuleParList PortInstance TimerInstance TimerList
-  VarInstance PortElementVarDef
+  VarInstance PortElementVarDef ClassMember
 %type <stmt_list> FunctionStatementOrDef ControlStatementOrDef
 
 %type <rangedef> RangeDef
@@ -1223,6 +1230,8 @@ CatchStatement
 CharStringMatch
 CharStringValue
 CheckStatement
+ClassDef
+ClassMemberList
 ClearStatement
 CommunicationStatements
 Complement
@@ -1467,11 +1476,15 @@ WithList
 WithStatement
 optAltstepFormalParList
 optAttribQualifier
+optBaseConstructorCall
+optClassMemberList
 optComponentDefList
 optDerivedDef
 optElseClause
 optExceptionSpec
+optExtendsClassDef
 optExtendsDef
+optFinallyDef
 optFromClause
 optFunctionActualParList
 optFunctionFormalParList
@@ -2345,6 +2358,7 @@ StructuredTypeDef: // 16
 | FunctionTypeDef { $$ = $1; }
 | AltstepTypeDef { $$ = $1; }
 | TestcaseTypeDef { $$ = $1; }
+| ClassDef { $$ = $1; }
 ;
 
 RecordDef: // 17
@@ -3493,6 +3507,140 @@ PortElement: // 86
   }
 ;
 
+ClassDef:
+  optExtKeyword ClassKeyword optFinalModifier optAbstractModifier IDentifier
+  optExtendsClassDef optRunsOnSpec optMtcSpec optSystemSpec '{'
+  optClassMemberList '}' optFinallyDef
+  {
+    ClassTypeBody* class_ = new ClassTypeBody($5, $1, $3, $4, $6, $7, $8, $9,
+      $11, $13);
+    class_->set_location(infile, @$);
+    Type* type = new Type(Type::T_CLASS, class_);
+    type->set_location(infile, @$);
+    $$ = new Def_Type($5, type);
+    $$->set_location(infile, @$);
+  }
+;
+
+optExtKeyword:
+  /* empty */ { $$ = false; }
+| ExtKeyword  { $$ = true; }
+;
+
+optFinalModifier:
+  /* empty */  { $$ = false; }
+| FinalKeyword { $$ = true; }
+;
+
+optAbstractModifier:
+  /* empty */     { $$ = false; }
+| AbstractKeyword { $$ = true; }
+;
+
+optExtendsClassDef:
+  /* empty */                   { $$ = NULL; }
+| ExtendsKeyword ReferencedType { $$ = $2; }
+;
+
+optFinallyDef:
+  /* empty */                   { $$ = NULL; }
+| FinallyKeyword StatementBlock { $$ = $2; }
+;
+
+optClassMemberList:
+  /* empty */     { $$ = new Definitions; }
+| ClassMemberList { $$ = $1; }
+;
+
+ClassMemberList:
+  optVisibility ClassMember optWithStatementAndSemiColon
+  {
+    $$ = new Definitions;
+    for (size_t i = 0; i < $2.nElements; ++i) {
+      $2.elements[i]->set_visibility($1.visibility);
+      $$->add_ass($2.elements[i]);
+    }
+    Free($2.elements);
+    // TODO: 'with' attributes
+  }
+| ClassMemberList optVisibility ClassMember optWithStatementAndSemiColon
+  {
+    $$ = $1;
+    for (size_t i = 0; i < $3.nElements; ++i) {
+      $3.elements[i]->set_visibility($2.visibility);
+      $$->add_ass($3.elements[i]);
+    }
+    Free($3.elements);
+    // TODO: 'with' attributes
+  }
+;
+
+ClassMember:
+  VarInstance { $$ = $1; }
+| TimerInstance { $$ = $1; }
+| ConstDef { $$ = $1; }
+| TemplateDef
+  {
+    $$.nElements = 1;
+    $$.elements = (Definition**)Malloc(sizeof(Definition*));
+    $$.elements[0] = $1;
+  }
+| ClassFunctionDef
+  {
+    $$.nElements = 1;
+    $$.elements = (Definition**)Malloc(sizeof(Definition*));
+    $$.elements[0] = $1;
+  }
+| ConstructorDef
+  {
+    $$.nElements = 1;
+    $$.elements = (Definition**)Malloc(sizeof(Definition*));
+    $$.elements[0] = $1;
+  }
+;
+
+ClassFunctionDef:
+  FunctionKeyword optFinalModifier
+  optDeterministicModifier IDentifier '(' optFunctionFormalParList ')'
+  optReturnType optError StatementBlock
+  {
+    $$ = new Def_Function($3, $4, $6, NULL, NULL, NULL, NULL, $8.type,
+      $8.returns_template, $8.template_restriction, $10);
+    $$->set_location(infile, @$);
+  }
+| FunctionKeyword optFinalModifier AbstractKeyword
+  optDeterministicModifier IDentifier '(' optFunctionFormalParList ')'
+  optReturnType optError
+  {
+    $$ = new Def_AbsFunction($4, $5, $7, $9.type, $9.returns_template,
+      $9.template_restriction);
+    $$->set_location(infile, @$);
+  }
+| ExtKeyword FunctionKeyword optFinalModifier optAbstractModifier
+  optDeterministicModifier IDentifier '(' optFunctionFormalParList ')'
+  optReturnType
+  {
+    $$ = new Def_ExtFunction($5, $6, $8, $10.type, $10.returns_template,
+      $10.template_restriction);
+    $$->set_location(infile, @$);
+  }
+;
+
+ConstructorDef:
+  CreateKeyword '(' optFunctionFormalParList ')' optBaseConstructorCall
+  StatementBlock
+  {
+    $$ = new Def_Constructor($3, $5, $6);
+    $$->set_location(infile, @$);
+  }
+;
+
+optBaseConstructorCall:
+  /* empty */          { $$ = NULL; }
+| ':' FunctionInstance { $$ = $2; }
+;
+
+
 /* A.1.6.1.2 Constant definitions */
 
 ConstDef: // 88
diff --git a/core/Makefile b/core/Makefile
index 02576d66f..09a5cfb1d 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -204,7 +204,7 @@ Module_list.hh Parameters.h Addfunc.hh RAW.hh BER.hh TEXT.hh ASN_Null.hh \
 ASN_Any.hh ASN_External.hh ASN_EmbeddedPDV.hh ASN_CharacterString.hh XER.hh \
 XmlReader.hh cversion.h TitanLoggerControl.ttcn TitanLoggerApi.xsd Vector.hh \
 JSON.hh Profiler.hh RefdIndex.hh ProfilerTools.hh Debugger.hh DebugCommands.hh \
-DebuggerUI.hh OER.hh
+DebuggerUI.hh OER.hh OOP.hh
 # Copied during "make install"
 
 ifdef REGEX_DIR
diff --git a/core/OOP.hh b/core/OOP.hh
new file mode 100644
index 000000000..1aabb52d6
--- /dev/null
+++ b/core/OOP.hh
@@ -0,0 +1,149 @@
+/******************************************************************************
+ * Copyright (c) 2000-2019 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+ *
+ * Contributors:
+ *   Baranyi, Botond
+ *
+ ******************************************************************************/
+
+#ifndef OOP_HH
+#define OOP_HH
+
+#include "Universal_charstring.hh"
+
+// OBJECT
+// ------
+
+class OBJECT {
+public:
+  boolean operator==(const OBJECT&) const { return TRUE; }
+  UNIVERSAL_CHARSTRING toString() const {
+    return UNIVERSAL_CHARSTRING("Object");
+  }
+};
+
+// OBJECT_REF
+// ----------
+
+template <typename T>
+class OBJECT_REF {
+public:
+  struct object_ref_struct {
+    T* obj_ptr;
+    size_t ref_count;
+  };
+  
+private:
+  object_ref_struct* val_ptr; // NULL if it's a null reference
+  
+  void clean_up() {
+    if (val_ptr != NULL) {
+      --val_ptr->ref_count;
+      if (val_ptr->ref_count == 0) {
+        delete val_ptr->obj_ptr;
+        delete val_ptr;
+      }
+      val_ptr = NULL;
+    }
+  }
+  
+public:
+  OBJECT_REF(): val_ptr(NULL) {} // constructor for null reference
+  
+  OBJECT_REF(T* p_obj_ptr): val_ptr(new object_ref_struct) { // constructor for new value (.create)
+    val_ptr->obj_ptr = p_obj_ptr;
+    val_ptr->ref_count = 1;
+  }
+  
+  OBJECT_REF(const OBJECT_REF<T>& p_other): val_ptr(p_other.val_ptr) { // copy constructor
+    if (val_ptr != NULL) {
+      ++val_ptr->ref_count;
+    }
+  }
+  
+  ~OBJECT_REF() {
+    clean_up();
+  }
+  
+  OBJECT_REF& operator=(null_type) { // assignment operator for null reference
+    clean_up();
+  }
+  
+  OBJECT_REF& operator=(const OBJECT_REF<T>& p_other) { // assignment operator for actual reference
+    clean_up();
+    if (p_other.val_ptr != NULL) {
+      val_ptr = p_other.val_ptr;
+      ++val_ptr->ref_count;
+    }
+    return *this;
+  }
+  
+  boolean operator==(const OBJECT_REF<T>& p_other) const { // equality operator
+    if (val_ptr == p_other.val_ptr) return TRUE;
+    if (val_ptr == NULL || p_other.val_ptr == NULL) return FALSE;
+    return *val_ptr->obj_ptr == *p_other.val_ptr->obj_ptr;
+  }
+  
+  boolean operator!=(const OBJECT_REF<T>& p_other) const { // inequality operator
+    return !(*this == p_other);
+  }
+  
+  T* operator*() { // de-referencing operator
+    if (val_ptr != NULL) {
+      return val_ptr->obj_ptr;
+    }
+    TTCN_error("Accessing a null reference.");
+  }
+  
+  T* operator->() { // de-referencing operator (for methods)
+    if (val_ptr != NULL) {
+      return val_ptr->obj_ptr;
+    }
+    TTCN_error("Accessing a null reference.");
+  }
+  
+  const T* operator->() const { // de-referencing operator (for constant methods)
+    if (val_ptr != NULL) {
+      return val_ptr->obj_ptr;
+    }
+    TTCN_error("Accessing a null reference.");
+  }
+};
+
+// EXCEPTION
+// ---------
+
+template<typename T, int type_id>
+class EXCEPTION {
+public:
+  struct exception_struct {
+    T* val_ptr;
+    size_t ref_count;
+  };
+private:
+  exception_struct* ex_ptr;
+  EXCEPTION operator=(const EXCEPTION&); // assignment disabled
+public:
+  EXCEPTION(T* p_value): ex_ptr(new exception_struct) {
+    ex_ptr->val_ptr = p_value;
+    ex_ptr->ref_count = 1;
+  }
+  EXCEPTION(const EXCEPTION& p): ex_ptr(p.ex_ptr) {
+    ++ex_ptr->ref_count;
+  }
+  ~EXCEPTION() {
+    --ex_ptr->ref_count;
+    if (ex_ptr->ref_count == 0) {
+      delete ex_ptr->val_ptr;
+      delete ex_ptr;
+    }
+  }
+  T& operator()() { return *ex_ptr->val_ptr; }
+};
+
+#endif /* OOP_HH */
+
diff --git a/core/TTCN3.hh b/core/TTCN3.hh
index c52c693f5..cab18c969 100644
--- a/core/TTCN3.hh
+++ b/core/TTCN3.hh
@@ -93,5 +93,6 @@
 #include "XmlReader.hh"
 #include "Profiler.hh"
 #include "Debugger.hh"
+#include "OOP.hh"
 
 #endif
diff --git a/regression_test/oop/.gitignore b/regression_test/oop/.gitignore
new file mode 100644
index 000000000..326407d63
--- /dev/null
+++ b/regression_test/oop/.gitignore
@@ -0,0 +1,5 @@
+oop
+oop.exe
+oop*.cc
+oop*.hh
+oop*.log
diff --git a/regression_test/oop/ExternalClass.hh b/regression_test/oop/ExternalClass.hh
new file mode 100644
index 000000000..c3eca41f1
--- /dev/null
+++ b/regression_test/oop/ExternalClass.hh
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright (c) 2000-2019 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+ *
+ * Contributors:
+ *   Baranyi, Botond
+ *
+ ******************************************************************************/
+
+#include <TTCN3.hh>
+
+#ifndef EXTERNALCLASS_HH
+#define EXTERNALCLASS_HH
+
+namespace oop {
+
+class ExternalClass : public OBJECT {
+public:
+  CHARSTRING f__ext(const INTEGER& x) {
+    return int2str(x) + CHARSTRING("?");
+  }
+};
+
+} // namespace
+
+#endif
diff --git a/regression_test/oop/Makefile b/regression_test/oop/Makefile
new file mode 100644
index 000000000..466453426
--- /dev/null
+++ b/regression_test/oop/Makefile
@@ -0,0 +1,55 @@
+##############################################################################
+# Copyright (c) 2000-2019 Ericsson Telecom AB
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+#
+# Contributors:
+#   Baranyi, Botond
+#
+##############################################################################
+TOPDIR := ..
+include $(TOPDIR)/Makefile.regression
+
+.SUFFIXES: .ttcn .asn .hh
+.PHONY: all clean dep run
+
+TTCN3_LIB = ttcn3$(RT2_SUFFIX)$(DYNAMIC_SUFFIX)
+
+TTCN3_MODULES = oop.ttcn
+
+GENERATED_SOURCES = $(ASN1_MODULES:.asn=.cc) $(TTCN3_MODULES:.ttcn=.cc)
+
+GENERATED_HEADERS = $(GENERATED_SOURCES:.cc=.hh)
+ifdef CODE_SPLIT
+GENERATED_SOURCES := $(foreach file, $(GENERATED_SOURCES:.cc=), $(addprefix $(file), .cc _seq.cc _set.cc  _seqof.cc _setof.cc _union.cc))
+else ifdef SPLIT_TO_SLICES
+POSTFIXES := $(foreach file, $(SPLIT_TO_SLICES), $(addsuffix $(file), _part_))
+POSTFIXES := $(foreach file, $(POSTFIXES), $(addprefix $(file), .cc))
+GENERATED_SOURCES2 := $(foreach file, $(GENERATED_SOURCES:.cc=), $(addprefix $(file), $(POSTFIXES)))
+GENERATED_SOURCES += $(GENERATED_SOURCES2)
+endif
+
+OBJECTS = $(GENERATED_SOURCES:.cc=.o)
+
+TARGET = oop$(EXESUFFIX)
+
+all: $(TARGET)
+
+$(TARGET): $(GENERATED_SOURCES) $(USER_SOURCES)
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ -L$(TTCN3_DIR)/lib -l$(TTCN3_LIB) -L$(OPENSSL_DIR)/lib -lcrypto $($(PLATFORM)_LIBS)
+
+$(GENERATED_SOURCES) $(GENERATED_HEADERS): compile
+
+compile: $(TTCN3_MODULES) $(ASN1_MODULES)
+	$(TTCN3_COMPILER) -k $(COMPILER_FLAGS) $^
+
+clean distclean:
+	$(RM) $(TARGET) $(GENERATED_HEADERS) $(GENERATED_SOURCES) $(OBJECTS) *.log Makefile.bak compile
+
+dep: $(GENERATED_SOURCES)
+	makedepend $(CPPFLAGS) $(GENERATED_SOURCES)
+
+run: $(TARGET)
+	./$(TARGET) oop.cfg
diff --git a/regression_test/oop/oop.cfg b/regression_test/oop/oop.cfg
new file mode 100644
index 000000000..d79be6074
--- /dev/null
+++ b/regression_test/oop/oop.cfg
@@ -0,0 +1,24 @@
+###############################################################################
+# Copyright (c) 2000-2019 Ericsson Telecom AB
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+#
+# Contributors:
+#   Baranyi, Botond
+#
+###############################################################################
+
+[LOGGING]
+FileMask := LOG_ALL | DEBUG | MATCHING
+ConsoleMask := ERROR | WARNING | TESTCASE | STATISTICS | PORTEVENT
+LogSourceInfo := Yes
+AppendFile := No
+TimeStampFormat := DateTime
+LogEventTypes := Yes
+SourceInfoFormat := Single
+LogEntityName := Yes
+
+[EXECUTE]
+oop.control
diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn
new file mode 100644
index 000000000..81e60c360
--- /dev/null
+++ b/regression_test/oop/oop.ttcn
@@ -0,0 +1,121 @@
+/******************************************************************************
+ * Copyright (c) 2000-2019 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
+ *
+ * Contributors:
+ *   Baranyi, Botond
+ *
+ ******************************************************************************/
+module oop {
+
+type component CT {
+  port PT pt;
+}
+
+type port PT message { inout integer; } with { extension "internal" }
+
+///////////////////////////////////////////////////
+///////////////////// CLASSES /////////////////////
+///////////////////////////////////////////////////
+
+type class BaseClass runs on CT mtc CT system CT {
+  public const integer m_const := 1;
+  /*protected*/ var charstring m_var;
+  private template octetstring m_temp := ? length (1..4);
+  public var template float m_var_temp;
+  //private PT m_port; // port members not supported
+  /*protected*/ timer m_timer;
+  private timer m_timer_array[3];
+  
+  //public template integer m_temp_pard(integer p) := p; // parameterized template members not supported
+  
+  // this would also be the implicit constructor (constructors are not yet implemented):
+  create(integer p_const, charstring p_var,
+         template octetstring p_temp, template float p_var_temp,
+         PT p_port, timer p_timer) {
+    m_const := p_const;
+    m_const := 2;
+    m_var := p_var;
+    m_temp := p_temp;
+    m_temp := *;
+    m_var_temp := p_var_temp;
+    m_port := p_port;
+    m_timer := p_timer;
+  }
+  
+  public function f(in integer x) return integer {
+    m_var := m_var & int2str(x);
+    return x;
+  }
+  
+  public function get_var() return charstring {
+    return m_var;
+  }
+}
+finally {
+  //pt.send(-1);
+  log(m_var);
+}
+
+type class SubClass extends BaseClass {
+  const octetstring m_const2 := 'AB'O; // the parser currently doesn't accept constants without initial value
+  
+  create(integer p_const := 1, charstring p_var,
+         template octetstring p_temp, template float p_var_temp,
+         PT p_port, timer p_timer)
+         : BaseClass(p_const, p_var, p_temp, p_var_temp, p_port, p_timer) {
+    m_const2 := 'FFFF'O;
+  }
+  
+  public function f(in integer x) return integer {
+    //return super.f(x) - 1; // not supported yet
+    m_var := m_var & int2str(x);
+    return x - 1;
+  }
+}
+
+type class @final FinalClass extends SubClass {
+  public function @final f(in integer x) return integer {
+    //return super.super.f(x) + 1; // not supported yet
+    m_var := m_var & int2str(x);
+    return x + 1;
+  }
+}
+
+
+type class @abstract AbstractClass {
+  public function @abstract f_abs(inout integer x) return boolean;
+  public function f_con(in charstring x) {
+    log(x);
+  }
+}
+
+type class ConcreteClass extends AbstractClass {
+  public function f_abs(inout integer x) return boolean {
+    x := x + 1;
+    return x > 0;
+  }
+}
+
+/* external classes don't work yet 
+type external class ExternalClass {
+  public external function f_ext(in integer x) return charstring;
+}
+
+type class InternalClass extends ExternalClass {
+  public function f_int() return integer {
+    return 0;
+  }
+  public external function f_ext2();
+}
+*/
+
+
+control {
+  log("nothing to run, yet...");
+}
+
+}
-- 
GitLab