diff --git a/compiler2/Value.cc b/compiler2/Value.cc
index 2d8a79be3515a404ec1c9b74598709c84dcc9c61..c04aebbf43b6fc65eaea158cf3f2de1471704b2f 100644
--- a/compiler2/Value.cc
+++ b/compiler2/Value.cc
@@ -398,6 +398,15 @@ namespace Common {
         u.expr.ti1 = p.u.expr.ti1->clone();
         u.expr.v2 = p.u.expr.v2->clone();
         break;
+      case OPTYPE_OF_CLASS:
+      case OPTYPE_CLASS_CASTING:
+        u.expr.type = p.u.expr.type->clone();
+        u.expr.r2 = p.u.expr.r2->clone();
+        break;
+      case OPTYPE_CLASS_CASTING_REF:
+        u.expr.r1 = p.u.expr.r1->clone();
+        u.expr.r2 = p.u.expr.r2->clone();
+        break;
       default:
         FATAL_ERROR("Value::Value()");
       } // switch
@@ -614,6 +623,14 @@ namespace Common {
             u.expr.v2->chk_expr_immutability();
             u.expr.ti1->chk_immutability();
             break;
+          case OPTYPE_OF_CLASS:
+          case OPTYPE_CLASS_CASTING:
+            u.expr.r2->chk_immutability();
+            break;
+          case OPTYPE_CLASS_CASTING_REF:
+            u.expr.r1->chk_immutability();
+            u.expr.r2->chk_immutability();
+            break;
           default:
             FATAL_ERROR("Value::chk_expr_immutability()");
         }
@@ -869,6 +886,7 @@ namespace Common {
       break;
     case OPTYPE_UNDEF_RUNNING: // r1 [r2] b4
     case OPTYPE_TMR_RUNNING: // r1 [r2] b4
+    case OPTYPE_CLASS_CASTING_REF:
       delete u.expr.r1;
       delete u.expr.r2;
       break;
@@ -991,6 +1009,11 @@ namespace Common {
     case OPTYPE_ANY2UNISTR:
       delete u.expr.logargs;
       break;
+    case OPTYPE_OF_CLASS:
+    case OPTYPE_CLASS_CASTING:
+      delete u.expr.type;
+      delete u.expr.r2;
+      break;
     default:
       FATAL_ERROR("Value::clean_up_expr()");
     } // switch
@@ -1784,6 +1807,13 @@ namespace Common {
       u.expr.v3=NULL;
       u.expr.v4 = NULL;
       break;
+    case OPTYPE_CLASS_CASTING_REF:
+      if (p_r1 == NULL || p_r2 == NULL) {
+        FATAL_ERROR("Value::Value()");
+      }
+      u.expr.r1 = p_r1;
+      u.expr.r2 = p_r2;
+      break;
     default:
       FATAL_ERROR("Value::Value()");
     } // switch
@@ -1873,6 +1903,26 @@ namespace Common {
       FATAL_ERROR("Value::Value()");
     } // switch
   }
+  
+  // type r2
+  Value::Value(operationtype_t p_optype, Type* p_type, Ttcn::Reference* p_r2)
+    : GovernedSimple(S_V), valuetype(V_EXPR), my_governor(0), in_brackets(false)
+  {
+    u.expr.v_optype = p_optype;
+    u.expr.state = EXPR_NOT_CHECKED;
+    switch (p_optype) {
+    case OPTYPE_OF_CLASS:
+    case OPTYPE_CLASS_CASTING:
+      if (p_type == NULL || p_r2 == NULL) {
+        FATAL_ERROR("Value::Value()");
+      }
+      u.expr.type = p_type;
+      u.expr.r2 = p_r2;
+      break;
+    default:
+      FATAL_ERROR("Value::Value()");
+    } // switch
+  }
 
   Value::~Value()
   {
@@ -2257,6 +2307,15 @@ namespace Common {
         u.expr.r2->set_fullname(p_fullname+".redirindex");
       }
       break;
+    case OPTYPE_OF_CLASS:
+    case OPTYPE_CLASS_CASTING:
+      u.expr.type->set_fullname(p_fullname + ".<classtype>");
+      u.expr.r2->set_fullname(p_fullname + ".<operand>");
+      break;
+    case OPTYPE_CLASS_CASTING_REF:
+      u.expr.r1->set_fullname(p_fullname + ".<operand2>");
+      u.expr.r2->set_fullname(p_fullname + ".<operand1>");
+      break;
     default:
       FATAL_ERROR("Value::set_fullname_expr()");
     } // switch
@@ -2515,6 +2574,15 @@ namespace Common {
     case OPTYPE_ANY2UNISTR:
       u.expr.logargs->set_my_scope(p_scope);
       break;
+    case OPTYPE_OF_CLASS:
+    case OPTYPE_CLASS_CASTING:
+      u.expr.type->set_my_scope(p_scope);
+      u.expr.r2->set_my_scope(p_scope);
+      break;
+    case OPTYPE_CLASS_CASTING_REF:
+      u.expr.r1->set_my_scope(p_scope);
+      u.expr.r2->set_my_scope(p_scope);
+      break;
     default:
       FATAL_ERROR("Value::set_my_scope_expr()");
     } // switch
@@ -2906,6 +2974,13 @@ namespace Common {
       case OPTYPE_ANY2UNISTR:
         u.expr.logargs->set_code_section(p_code_section);
         break;
+      case OPTYPE_CLASS_CASTING_REF:
+        u.expr.r1->set_code_section(p_code_section);
+        // no break
+      case OPTYPE_CLASS_CASTING:
+      case OPTYPE_OF_CLASS:
+        u.expr.r2->set_code_section(p_code_section);
+        break;
       default:
         FATAL_ERROR("Value::set_code_section()");
       } // switch
@@ -3653,6 +3728,7 @@ namespace Common {
       case OPTYPE_CHECKSTATE_ANY:
       case OPTYPE_CHECKSTATE_ALL:
       case OPTYPE_ISTEMPLATEKIND:
+      case OPTYPE_OF_CLASS:
         return Type::T_BOOL;
       case OPTYPE_GETVERDICT:
         return Type::T_VERDICT;
@@ -3935,6 +4011,9 @@ namespace Common {
         return Type::T_OSTR;
       case OPTYPE_DECOMP:
         return Type::T_OID;
+      case OPTYPE_CLASS_CASTING:
+      case OPTYPE_CLASS_CASTING_REF:
+        return Type::T_CLASS;
       default:
         FATAL_ERROR("Value::get_expr_returntype(): invalid optype");
         // to avoid warning
@@ -4116,6 +4195,11 @@ namespace Common {
       case OPTYPE_GET_PORT_REF:
         chk_expr_operands(NULL, exp_val); // calculate the port type
         return u.expr.type;
+      case OPTYPE_CLASS_CASTING_REF:
+        chk_expr_operands(NULL, exp_val);
+        // no break
+      case OPTYPE_CLASS_CASTING:
+        return u.expr.type;
       default:
         break;
       }
@@ -4411,6 +4495,11 @@ namespace Common {
       return "@profiler.running";
     case OPTYPE_GET_PORT_REF:
       return "port.getref()";
+    case OPTYPE_OF_CLASS:
+      return "of";
+    case OPTYPE_CLASS_CASTING:
+    case OPTYPE_CLASS_CASTING_REF:
+      return "=>";
     default:
       FATAL_ERROR("Value::get_opname()");
     } // switch
@@ -8347,6 +8436,69 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
       if (!governor) return;
       chk_expr_eval_ti(u.expr.ti1, governor, refch, ti_exp_val);
     } break;
+    case OPTYPE_CLASS_CASTING_REF:
+      {
+        Error_Context cntxt(this, "In the second operand of operation `%s'", opname);
+        Type* type = u.expr.r1->chk_variable_ref();
+        type = (type == NULL) ? new Type(Type::T_ERROR) : type->clone();
+        type->set_my_scope(my_scope);
+        type->set_fullname(get_fullname() + ".<classtype>");
+        type->set_location(*u.expr.r1);
+        delete u.expr.r1;
+        u.expr.type = type;
+        u.expr.v_optype = OPTYPE_CLASS_CASTING;
+      }
+      // no break
+    case OPTYPE_CLASS_CASTING:
+    case OPTYPE_OF_CLASS: {
+      bool erroneous = false;
+      Type* ref_type_last = NULL;
+      {
+        Error_Context cntxt(this, "In the first operand of operation `%s'", opname);
+        Type* ref_type = u.expr.r2->chk_variable_ref();
+        if (ref_type != NULL) {
+          ref_type_last = ref_type->get_type_refd_last();
+          if (ref_type_last->get_typetype() != Type::T_CLASS) {
+            u.expr.r2->error("Reference to a class object was expected");
+            erroneous = true;
+          }
+        }
+        else {
+          erroneous = true;
+        }
+      }
+      {
+        Error_Context cntxt(this, "In the second operand of operation `%s'", opname);
+        u.expr.type->chk();
+        Type* type_last = u.expr.type->get_type_refd_last();
+        if (type_last->get_typetype() != Type::T_CLASS) {
+          if (type_last->get_typetype() != Type::T_ERROR) {
+            u.expr.type->error("Class type was expected");
+          }
+          erroneous = true;
+        }
+        else if (u.expr.v_optype == OPTYPE_CLASS_CASTING) {
+          Ttcn::ClassTypeBody* new_class = type_last->get_class_type_body();
+          if (new_class->is_abstract()) {
+            u.expr.type->error("Cannot cast to abstract class type `%s'",
+              u.expr.type->get_typename().c_str());
+          }
+          if (!erroneous) {
+            Ttcn::ClassTypeBody* old_class = ref_type_last->get_class_type_body();
+            if (!new_class->is_parent_class(old_class) &&
+                !old_class->is_parent_class(new_class)) {
+              u.expr.type->error("Cannot cast an object of class type `%s' "
+                "to class type `%s'",
+                ref_type_last->get_typename().c_str(),
+                u.expr.type->get_typename().c_str());
+            }
+          }
+        }
+      }
+      if (erroneous) {
+        set_valuetype(V_ERROR);
+      }
+      break; }
     default:
       FATAL_ERROR("chk_expr_operands()");
     } // switch optype
@@ -9293,6 +9445,25 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
         delete v1;
       }
       break;
+    case OPTYPE_OF_CLASS:
+      if (!is_unfoldable()) {
+        clean_up();
+        valuetype = V_BOOL;
+        u.val_bool = false;
+      }
+      break;
+    case OPTYPE_CLASS_CASTING:
+      if (!is_unfoldable()) {
+        Type* type = u.expr.type;
+        Reference* ref = u.expr.r2;
+        valuetype = V_REFD;
+        u.ref.ref = ref;
+        u.ref.refd_last = this;
+        set_my_governor(type->get_type_refd_last());
+        delete type;
+      }
+      break;
+    case OPTYPE_CLASS_CASTING_REF:
     case OPTYPE_UNDEF_RUNNING:
     default:
       FATAL_ERROR("Value::evaluate_value()");
@@ -9904,6 +10075,19 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
       case OPTYPE_ANY2UNISTR:
       case OPTYPE_TTCN2STRING:
         return true;
+      case OPTYPE_OF_CLASS: {
+        // class of object reference
+        Ttcn::ClassTypeBody* ref_class = u.expr.r2->chk_variable_ref()->
+          get_type_refd_last()->get_class_type_body();
+        // expected class
+        Ttcn::ClassTypeBody* exp_class = u.expr.type->get_type_refd_last()->
+          get_class_type_body();
+        // the result is always false if the classes are not related to each other,
+        // otherwise a runtime check is required
+        return ref_class->is_parent_class(exp_class) ||
+          exp_class->is_parent_class(ref_class); }
+      case OPTYPE_CLASS_CASTING:
+        return !u.expr.type->is_identical(u.expr.r2->chk_variable_ref());
       default:
         FATAL_ERROR("Value::is_unfoldable()");
       } // switch
@@ -11661,9 +11845,15 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
       self_ref |= chk_expr_self_ref_val(u.expr.v1, lhs);
       // TODO t_list2
       break;
+    case OPTYPE_OF_CLASS:
+    case OPTYPE_CLASS_CASTING: {
+      Common::Assignment *ass = u.expr.r2->get_refd_assignment();
+      self_ref |= (ass == lhs);
+      break; }
 
     case NUMBER_OF_OPTYPES: // can never happen
     case OPTYPE_ISCHOSEN: // r1 i2, should have been classified as _T or _V
+    case OPTYPE_CLASS_CASTING_REF:
       FATAL_ERROR("Value::chk_expr_self_ref(%d)", u.expr.v_optype);
       break;
     } // switch u.expr.v_optype
@@ -12301,6 +12491,15 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
         return string("@profiler.running");
       case OPTYPE_GET_PORT_REF:
         return string("port.getref()");
+      case OPTYPE_OF_CLASS:
+        return u.expr.r2->get_dispname() + string(" of ") +
+          u.expr.type->get_typename();
+      case OPTYPE_CLASS_CASTING:
+        return u.expr.r2->get_dispname() + string(" => ") +
+          u.expr.type->get_typename();
+      case OPTYPE_CLASS_CASTING_REF:
+        return u.expr.r2->get_dispname() + string(" => (") +
+          u.expr.r1->get_dispname() + string(")");
       default:
         return string("<unsupported optype>");
       } // switch u.expr.v_optype
@@ -13919,6 +14118,41 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
         u.expr.type->get_typename().c_str());
       expr->expr = mputprintf(expr->expr, "(*%s)", tmp_id.c_str());
       break; }
+    case OPTYPE_OF_CLASS: {
+      Ttcn::ClassTypeBody* exp_class = u.expr.type->get_type_refd_last()->
+        get_class_type_body();
+      expression_struct_t ref_expr;
+      Code::init_expr(&ref_expr);
+      u.expr.r2->generate_code(&ref_expr);
+      if (ref_expr.preamble != NULL) {
+        expr->preamble = mputstr(expr->preamble, ref_expr.preamble);
+      }
+      expr->expr = mputprintf(expr->expr,
+        "%s != NULL_VALUE && dynamic_cast<%s*>(*%s) != NULL",
+        ref_expr.expr, exp_class->is_built_in() ? "OBJECT" :
+          u.expr.type->get_type_refd_last()->get_genname_own(my_scope).c_str(),
+        ref_expr.expr);
+      if (ref_expr.postamble != NULL) {
+        expr->postamble = mputstr(expr->postamble, ref_expr.postamble);
+      }
+      Code::free_expr(&ref_expr);
+      break; }
+    case OPTYPE_CLASS_CASTING: {
+      expression_struct_t ref_expr;
+      Code::init_expr(&ref_expr);
+      u.expr.r2->generate_code(&ref_expr);
+      if (ref_expr.preamble != NULL) {
+        expr->preamble = mputstr(expr->preamble, ref_expr.preamble);
+      }
+      expr->expr = mputprintf(expr->expr,
+        "%s.cast_to<%s>()",
+        ref_expr.expr,
+        u.expr.type->get_type_refd_last()->get_genname_own(my_scope).c_str());
+      if (ref_expr.postamble != NULL) {
+        expr->postamble = mputstr(expr->postamble, ref_expr.postamble);
+      }
+      Code::free_expr(&ref_expr);
+      break; }
     default:
       FATAL_ERROR("Value::generate_code_expr_expr()");
     }
@@ -15770,6 +16004,9 @@ void Value::chk_expr_operand_execute_refd(Value *v1,
     case OPTYPE_EXECUTE_REFD: // v1 ap_list2 [v3]
       return has_single_expr_invoke(u.expr.v1, u.expr.ap_list2) &&
             (!u.expr.v3 || u.expr.v3->has_single_expr());
+    case OPTYPE_OF_CLASS:
+    case OPTYPE_CLASS_CASTING:
+      return u.expr.r2->has_single_expr();
     default:
       FATAL_ERROR("Value::has_single_expr_expr()");
     } // switch
diff --git a/compiler2/Value.hh b/compiler2/Value.hh
index 0581f6f69751538909d4cede5b29853772796212..5e24451ee17218457b92e4426a311e76efb9091e 100644
--- a/compiler2/Value.hh
+++ b/compiler2/Value.hh
@@ -279,11 +279,15 @@ namespace Common {
       OPTYPE_CBOR2JSON, // v1
       OPTYPE_JSON2CBOR, // v1
       OPTYPE_BSON2JSON, // v1
-      OPTYPE_JSON2BSON, // v1
+      OPTYPE_JSON2BSON, // v1         120
       
       OPTYPE_GET_PORT_REF, // -
+      
+      OPTYPE_OF_CLASS, // type r2
+      OPTYPE_CLASS_CASTING, // type r2
+      OPTYPE_CLASS_CASTING_REF, // r1 r2
 
-      NUMBER_OF_OPTYPES // must be last              122
+      NUMBER_OF_OPTYPES // must be last              125
     };
 
     enum macrotype_t {
@@ -474,7 +478,7 @@ namespace Common {
     Value(valuetype_t p_vt, Reference *p_ref);
     Value(valuetype_t p_vt, Block *p_block);
     Value(valuetype_t p_vt, verdict_t p_verdict);
-    /** Constructor used by decode */
+    /** Constructor used by V_EXPR "r1 r2": DECODE, OPTYPE_CLASS_CASTING_REF */
     Value(operationtype_t p_optype, Ttcn::Reference *p_r1, Ttcn::Reference *p_r2);
     /** Constructor used by decvalue_unichar*/
     Value(operationtype_t p_optype, Ttcn::Reference *p_r1, Ttcn::Reference *p_r2, Value *p_v3);
@@ -486,6 +490,8 @@ namespace Common {
       Value* p_v3, Value* p_v4, Value* p_v5);
     /** Constructor used by V_ANY_VALUE and V_ANY_OR_OMIT */
     Value(valuetype_t p_vt, Ttcn::LengthRestriction* p_len_res);
+    /** Constructor used by V_EXPR "type r2": OF_CLASS, OPTYPE_CLASS_CASTING */
+    Value(operationtype_t p_optype, Type* p_type, Ttcn::Reference* p_r2);
     virtual ~Value();
     virtual Value* clone() const;
     valuetype_t get_valuetype() const {return valuetype;}
diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index 1ea4a48d0db0c7290442ae0cc7b516bbcb5c2ec4..abf2ea2f39c66e68981ef589c5c3dc1a2287cc7a 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -6428,7 +6428,8 @@ namespace Ttcn {
   }
 
   Def_Function_Base::Def_Function_Base(const Def_Function_Base& p)
-    : Definition(p), prototype(PROTOTYPE_NONE), input_type(0), output_type(0)
+    : Definition(p), prototype(PROTOTYPE_NONE), input_type(0), output_type(0),
+    final(p.final)
   {
     fp_list = p.fp_list->clone();
     fp_list->set_my_def(this);
@@ -6438,11 +6439,11 @@ namespace Ttcn {
 
   Def_Function_Base::Def_Function_Base(bool is_external, Identifier *p_id,
     FormalParList *p_fpl, Type *p_return_type, bool returns_template,
-    template_restriction_t p_template_restriction)
+    template_restriction_t p_template_restriction, bool p_final)
     : Definition(determine_asstype(is_external, p_return_type != 0,
         returns_template), p_id), fp_list(p_fpl), return_type(p_return_type),
         prototype(PROTOTYPE_NONE), input_type(0), output_type(0),
-        template_restriction(p_template_restriction)
+        template_restriction(p_template_restriction), final(p_final)
   {
     if (!p_fpl) FATAL_ERROR("Def_Function_Base::Def_Function_Base()");
     fp_list->set_my_def(this);
@@ -6662,9 +6663,9 @@ namespace Ttcn {
                              Type *p_return_type,
                              bool returns_template,
                              template_restriction_t p_template_restriction,
-                             StatementBlock *p_block)
+                             bool p_final, StatementBlock *p_block)
     : Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template,
-        p_template_restriction),
+        p_template_restriction, p_final),
         runs_on_ref(p_runs_on_ref), runs_on_type(0),
         mtc_ref(p_mtc_ref), mtc_type(0),
         system_ref(p_system_ref), system_type(0),
diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh
index 9341701f39d9715fc6bfd9561ec812cc3fb48662..2d7039ac1dd358e8abb3fa70180455deabd9a681 100644
--- a/compiler2/ttcn3/AST_ttcn3.hh
+++ b/compiler2/ttcn3/AST_ttcn3.hh
@@ -1356,6 +1356,7 @@ namespace Ttcn {
     Type *output_type;
     /** optional template restriction on return template value */
     template_restriction_t template_restriction;
+    bool final;
 
     static asstype_t determine_asstype(bool is_external, bool has_return_type,
       bool returns_template);
@@ -1376,7 +1377,7 @@ namespace Ttcn {
      */
     Def_Function_Base(bool is_external, Identifier *p_id,
       FormalParList *p_fpl, Type *p_return_type, bool returns_template,
-      template_restriction_t p_template_restriction);
+      template_restriction_t p_template_restriction, bool p_final);
     virtual ~Def_Function_Base();
     virtual void set_fullname(const string& p_fullname);
     virtual void set_my_scope(Scope *p_scope);
@@ -1393,6 +1394,7 @@ namespace Ttcn {
     bool is_identical(Def_Function_Base* p_other);
     template_restriction_t get_template_restriction()
       { return template_restriction; }
+    virtual bool is_final() const { return final; }
     /** Checks and returns whether the function is startable.
      * Reports the appropriate error message(s) if not. */
     //bool chk_startable(Location* caller_location);
@@ -1471,10 +1473,9 @@ namespace Ttcn {
     Def_Function(bool p_deterministic, Identifier *p_id, FormalParList *p_fpl,
                  Reference *p_runs_on_ref, Reference *p_mtc_ref,
                  Reference *p_system_ref, Reference *p_port_ref,
-                 Type *p_return_type,
-                 bool returns_template,
+                 Type *p_return_type, bool returns_template,
                  template_restriction_t p_template_restriction,
-                 StatementBlock *p_block);
+                 bool p_final, StatementBlock *p_block);
     virtual ~Def_Function();
     virtual Def_Function *clone() const;
     virtual void set_fullname(const string& p_fullname);
@@ -1570,9 +1571,9 @@ namespace Ttcn {
      */
     Def_ExtFunction(bool p_deterministic, Identifier *p_id, FormalParList *p_fpl,
       Type *p_return_type, bool returns_template,
-      template_restriction_t p_template_restriction, bool p_ext_keyword)
+      template_restriction_t p_template_restriction, bool p_final, bool p_ext_keyword)
       : Def_Function_Base(true, p_id, p_fpl, p_return_type, returns_template,
-          p_template_restriction),
+          p_template_restriction, p_final),
       function_type(EXTFUNC_MANUAL), encoding_type(Type::CT_UNDEF),
       encoding_options(0), eb_list(0), printing(0),
       deterministic(p_deterministic), ext_keyword(p_ext_keyword) { }
@@ -1617,7 +1618,7 @@ namespace Ttcn {
       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) { }
+      p_template_restriction, false) { }
     virtual ~Def_AbsFunction();
     virtual Definition* clone() const;
     virtual void chk();
diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc
index a7ffea6a497ceb9bb12b2686686905355abea7c1..503eea6bc5cc4198712e9e274892224836afcc13 100644
--- a/compiler2/ttcn3/Statement.cc
+++ b/compiler2/ttcn3/Statement.cc
@@ -550,6 +550,10 @@ namespace Ttcn {
       delete select_union.expr;
       delete select_union.sus;
       break;
+    case S_SELECT_CLASS:
+      delete select_class.ref;
+      delete select_class.sccs;
+      break;
     case S_FOR:
       if(loop.for_stmt.varinst)
         delete loop.for_stmt.init_varinst;
@@ -964,7 +968,7 @@ namespace Ttcn {
     } // switch statementtype
   }
   
-    Statement::Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus)
+  Statement::Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus)
     : statementtype(p_st), my_sb(0)
   {
     switch(statementtype) {
@@ -978,6 +982,22 @@ namespace Ttcn {
       FATAL_ERROR("Statement::Statement()");
     } // switch statementtype
   }
+    
+  Statement::Statement(statementtype_t p_st, Reference *p_ref, SelectClassCases *p_sccs)
+    : statementtype(p_st), my_sb(0)
+  {
+    switch(statementtype) {
+    case S_SELECT_CLASS:
+      if (p_ref == NULL || p_sccs == NULL) {
+        FATAL_ERROR("Statement::Statement()");
+      }
+      select_class.ref = p_ref;
+      select_class.sccs = p_sccs;
+      break;
+    default:
+      FATAL_ERROR("Statement::Statement()");
+    } // switch statementtype
+  }
 
   Statement::Statement(statementtype_t p_st, Definitions *p_defs,
                        Assignment *p_ass, Value *p_final,
@@ -1598,6 +1618,7 @@ namespace Ttcn {
     case S_IF: return "if";
     case S_SELECT: return "select-case";
     case S_SELECTUNION: return "select-union";
+    case S_SELECT_CLASS: return "select-class";
     case S_FOR: return "for";
     case S_WHILE: return "while";
     case S_DOWHILE: return "do-while";
@@ -1777,6 +1798,10 @@ namespace Ttcn {
       select_union.expr->set_my_scope(p_scope);
       select_union.sus->set_my_scope(p_scope);
       break;
+    case S_SELECT_CLASS:
+      select_class.ref->set_my_scope(p_scope);
+      select_class.sccs->set_my_scope(p_scope);
+      break;
     case S_FOR:
       if (loop.for_stmt.varinst) {
         loop.for_stmt.init_varinst->set_parent_scope(p_scope);
@@ -2086,6 +2111,10 @@ namespace Ttcn {
       select_union.expr->set_fullname(p_fullname+".expr");
       select_union.sus->set_fullname(p_fullname+".sus");
       break;
+    case S_SELECT_CLASS:
+      select_class.ref->set_fullname(p_fullname+".ref");
+      select_class.sccs->set_fullname(p_fullname+".sccs");
+      break;
     case S_FOR:
       if(loop.for_stmt.varinst)
         loop.for_stmt.init_varinst->set_fullname(p_fullname+".init");
@@ -2380,6 +2409,9 @@ namespace Ttcn {
     case S_SELECTUNION:
       select_union.sus->set_my_sb(p_sb, p_index);
       break;
+    case S_SELECT_CLASS:
+      select_class.sccs->set_my_sb(p_sb, p_index);
+      break;
     case S_FOR:
     case S_WHILE:
     case S_DOWHILE:
@@ -2413,6 +2445,9 @@ namespace Ttcn {
     case S_SELECTUNION:
       select_union.sus->set_my_def(p_def);
       break;
+    case S_SELECT_CLASS:
+      select_class.sccs->set_my_def(p_def);
+      break;
     case S_FOR:
     case S_WHILE:
     case S_DOWHILE:
@@ -2446,6 +2481,9 @@ namespace Ttcn {
     case S_SELECTUNION:
       select_union.sus->set_my_ags(p_ags);
       break;
+    case S_SELECT_CLASS:
+      select_class.sccs->set_my_ags(p_ags);
+      break;
     case S_FOR:
     case S_WHILE:
     case S_DOWHILE:
@@ -2476,6 +2514,9 @@ namespace Ttcn {
     case S_SELECTUNION:
       select_union.sus->set_my_laic_stmt(p_ags, p_loop_stmt);
       break;
+    case S_SELECT_CLASS:
+      select_class.sccs->set_my_laic_stmt(p_ags, p_loop_stmt);
+      break;
     case S_ALT:
     case S_INTERLEAVE:
       if (p_loop_stmt)
@@ -2548,6 +2589,8 @@ namespace Ttcn {
       return select.scs->has_return();
     case S_SELECTUNION:
       return select_union.sus->has_return();
+    case S_SELECT_CLASS:
+      return select_class.sccs->has_return();
     case S_FOR:
     case S_WHILE:
       if (loop.block->has_return() == StatementBlock::RS_NO)
@@ -2672,6 +2715,8 @@ namespace Ttcn {
       return select.scs->has_receiving_stmt();
     case S_SELECTUNION:
       return select_union.sus->has_receiving_stmt();
+    case S_SELECT_CLASS:
+      return select_class.sccs->has_receiving_stmt();
     case S_FOR:
     case S_WHILE:
     case S_DOWHILE:
@@ -2763,6 +2808,9 @@ namespace Ttcn {
     case S_SELECTUNION:
       chk_select_union();
       break;
+    case S_SELECT_CLASS:
+      chk_select_class();
+      break;
     case S_FOR:
       chk_for();
       break;
@@ -2989,6 +3037,9 @@ namespace Ttcn {
     case S_SELECTUNION:
       select_union.sus->chk_allowed_interleave();
       break;
+    case S_SELECT_CLASS:
+      select_class.sccs->chk_allowed_interleave();
+      break;
     case S_FOR:
     case S_WHILE:
     case S_DOWHILE:
@@ -3435,6 +3486,24 @@ error:
     
     select_union.sus->chk();
   }
+  
+  void Statement::chk_select_class()
+  {
+    Error_Context cntxt(this, "In select class statement");
+    Type* ref_type = select_class.ref->chk_variable_ref();
+    ClassTypeBody* ref_class = NULL;
+    if (ref_type != NULL) {
+      Type* ref_type_last = ref_type->get_type_refd_last();
+      if (ref_type_last->get_typetype() != Type::T_CLASS) {
+        select_class.ref->error("Reference to a class object was expected");
+      }
+      else {
+        ref_class = ref_type_last->get_class_type_body();
+      }
+    }
+    select_class.sccs->chk(ref_class);
+  }
+  
   void Statement::chk_for()
   {
     Error_Context cntxt(this, "In for statement");
@@ -6111,6 +6180,10 @@ error:
       select_union.expr->set_code_section(p_code_section);
       select_union.sus->set_code_section(p_code_section);
       break;
+    case S_SELECT_CLASS:
+      select_class.ref->set_code_section(p_code_section);
+      select_class.sccs->set_code_section(p_code_section);
+      break;
     case S_ALT:
     case S_INTERLEAVE:
       ags->set_code_section(p_code_section);
@@ -6360,6 +6433,7 @@ error:
     case S_IF:
     case S_SELECT:
     case S_SELECTUNION:
+    case S_SELECT_CLASS:
     case S_FOR:
     case S_WHILE:
     case S_DOWHILE:
@@ -6406,6 +6480,9 @@ error:
     case S_SELECTUNION:
       str=generate_code_select_union(str, def_glob_vars, src_glob_vars);
       break;
+    case S_SELECT_CLASS:
+      str = generate_code_select_class(str, def_glob_vars, src_glob_vars);
+      break;
     case S_FOR:
       str=generate_code_for(str, def_glob_vars, src_glob_vars);
       break;
@@ -6707,6 +6784,9 @@ error:
     case S_SELECTUNION:
       ilt_generate_code_select_union(ilt);
       break;
+    case S_SELECT_CLASS:
+      ilt_generate_code_select_class(ilt);
+      break;
     case S_CALL:
       ilt_generate_code_call(ilt);
       break;
@@ -6983,6 +7063,26 @@ error:
     return str;
   }
   
+  char* Statement::generate_code_select_class(char* str, char*& def_glob_vars, char*& src_glob_vars)
+  {
+    const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id();
+    str = mputstr(str, "{\n");
+    expression_struct_t expr;
+    Code::init_expr(&expr);
+    select_class.ref->generate_code(&expr);
+    if (expr.preamble != NULL) {
+      str = mputstr(str, expr.preamble);
+    }
+    str = select_class.sccs->generate_code(str, def_glob_vars, src_glob_vars,
+      tmp_prefix.c_str(), expr.expr);
+    if (expr.postamble != NULL) {
+      str = mputstr(str, expr.postamble);
+    }
+    Code::free_expr(&expr);
+    str = mputstr(str, "}\n");
+    return str;
+  }
+  
   char *Statement::generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars)
   {
     /** \todo initial does not have its own location */
@@ -7362,6 +7462,30 @@ error:
       str = mputstr(str, "}\n");
     }
   }
+  
+  void Statement::ilt_generate_code_select_class(ILT* ilt)
+  {
+    const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id();
+    char*& str = ilt->get_out_branches();
+    expression_struct expr;
+    Code::init_expr(&expr);
+    select_class.ref->generate_code(&expr);
+    bool has_recv = select_class.sccs->has_receiving_stmt();
+    if (has_recv) {
+      str = mputstr(str, "{\n");
+    }
+    if (expr.preamble != NULL) {
+      str = mputstr(str, expr.preamble);
+    }
+    select_class.sccs->ilt_generate_code(ilt, tmp_prefix.c_str(), expr.expr);
+    if (expr.postamble != NULL) {
+      str = mputstr(str, expr.postamble);
+    }
+    Code::free_expr(&expr);
+    if (has_recv) {
+      str = mputstr(str, "}\n");
+    }
+  }
 
   void Statement::ilt_generate_code_call(ILT *ilt)
   {
@@ -8675,6 +8799,9 @@ error:
         case S_SELECTUNION:
           select_union.sus->set_parent_path(p_path);
           break;
+        case S_SELECT_CLASS:
+          select_class.sccs->set_parent_path(p_path);
+          break;
         case S_FOR:
           loop.block->set_parent_path(p_path);
           break;
@@ -12870,6 +12997,348 @@ error:
       sus[i]->set_parent_path(p_path);
     }
   }
+  
+  // =================================
+  // ===== SelectClassCase
+  // =================================
+
+  SelectClassCase::SelectClassCase(Common::Type* p_type, StatementBlock* p_block)
+    : type(p_type), block(p_block), my_scope(NULL), always_false(false)
+  {
+    if (block == NULL) {
+      FATAL_ERROR("SelectClassCase::SelectClassCase()");
+    }
+  }
+
+  SelectClassCase::~SelectClassCase()
+  {
+    delete type;
+    delete block;
+  }
+
+  SelectClassCase* SelectClassCase::clone() const
+  {
+    FATAL_ERROR("SelectClassCase::clone");
+  }
+
+  void SelectClassCase::set_my_scope(Scope* p_scope)
+  {
+    my_scope = p_scope;
+    if (type != NULL) {
+      type->set_my_scope(p_scope);
+    }
+    block->set_my_scope(p_scope);
+  }
+
+  void SelectClassCase::set_fullname(const string& p_fullname)
+  {
+    Node::set_fullname(p_fullname);
+    if (type != NULL) {
+      type->set_fullname(p_fullname + ".type");
+    }
+    block->set_fullname(p_fullname + ".block");
+  }
+
+  void SelectClassCase::chk(ClassTypeBody* p_ref_class, bool& unreach)
+  {
+    Error_Context cntxt(this, "In select class case statement");
+    if (unreach) {
+      warning("Control never reaches this code because of previous "
+        "effective case(s)");
+    }
+    if (type != NULL) {
+      type->chk();
+      Common::Type* type_last = type->get_type_refd_last();
+      if (type_last->get_typetype() != Type::T_CLASS) {
+        type->error("Class type was expected");
+      }
+      else if (!unreach) {
+        ClassTypeBody* class_ = type_last->get_class_type_body();
+        if (p_ref_class != NULL && !class_->is_parent_class(p_ref_class) &&
+            !p_ref_class->is_parent_class(class_)) {
+          block->warning("Control never reaches this code because the case will "
+            "never be chosen");
+          always_false = true;
+        }
+      }
+    }
+    else {
+      unreach = true; // else statement
+    }
+    block->chk();
+  }
+
+  void SelectClassCase::set_code_section(GovernedSimple::code_section_t p_code_section)
+  {
+    block->set_code_section(p_code_section);
+  }
+
+  char* SelectClassCase::generate_code_if(char* str, const char* tmp_prefix,
+                                          const char* ref_str, size_t idx,
+                                          bool& unreach)
+  {
+    if (unreach || always_false) {
+      return str;
+    }
+    if (type != NULL) {
+      ClassTypeBody* class_ = type->get_type_refd_last()->get_class_type_body();
+      str = mputprintf(str,
+        "if (%s != NULL_VALUE && dynamic_cast<%s*>(*%s) != NULL) goto %s_%lu;\n",
+        ref_str, class_->is_built_in() ? "OBJECT" :
+          type->get_type_refd_last()->get_genname_own(my_scope).c_str(),
+        ref_str, tmp_prefix, static_cast<unsigned long>(idx));
+    }
+    else {
+      unreach = true; // else statement
+      str = mputprintf(str, "goto %s_%lu;\n",
+        tmp_prefix, static_cast<unsigned long>(idx));
+    }
+    return str;
+  }
+
+  char* SelectClassCase::generate_code_stmt(char* str, char*& def_glob_vars, char*& src_glob_vars,
+                                            const char* tmp_prefix, size_t idx, bool& unreach)
+  {
+    if (unreach || always_false) {
+      return str;
+    }
+    if (type == NULL) {
+      unreach = true;
+    }
+    str = mputprintf(str, "%s_%lu:\n{\n",
+      tmp_prefix, static_cast<unsigned long>(idx));
+    if (debugger_active) {
+      str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
+    }
+    str = block->generate_code(str, def_glob_vars, src_glob_vars);
+    str = mputprintf(str, "goto %s_end;\n}\n", tmp_prefix);
+    return str;
+  }
+
+  void SelectClassCase::ilt_generate_code_stmt(ILT* ilt, const char* tmp_prefix,
+                                               size_t idx, bool& unreach)
+  {
+    if (unreach || always_false) {
+      return;
+    }
+    if (type == NULL) {
+      unreach = true;
+    }
+    char*& str = ilt->get_out_branches();
+    str = mputprintf(str, "%s_%lu:\n",
+      tmp_prefix, static_cast<unsigned long>(idx));
+    bool has_recv = block->has_receiving_stmt();
+    if (!has_recv) {
+      str = mputstr(str, "{\n");
+      str = block->generate_code(str, ilt->get_out_def_glob_vars(),
+        ilt->get_out_src_glob_vars());
+    }
+    else {
+      block->ilt_generate_code(ilt);
+    }
+    str = mputprintf(str, "goto %s_end;\n", tmp_prefix);
+    if (!has_recv) {
+      str = mputstr(str, "}\n");
+    }
+  }
+
+  void SelectClassCase::set_parent_path(WithAttribPath* p_path) {
+    block->set_parent_path(p_path);
+  }
+
+  // =================================
+  // ===== SelectClassCases
+  // =================================
+
+  SelectClassCases::~SelectClassCases()
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      delete sccs[i];
+    }
+    sccs.clear();
+  }
+
+  SelectClassCases *SelectClassCases::clone() const
+  {
+    FATAL_ERROR("SelectClassCases::clone");
+  }
+
+  void SelectClassCases::add_scc(SelectClassCase *p_scc)
+  {
+    if (p_scc == NULL) {
+      FATAL_ERROR("SelectClassCases::add_scc()");
+    }
+    sccs.add(p_scc);
+  }
+
+  void SelectClassCases::set_my_scope(Scope* p_scope)
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->set_my_scope(p_scope);
+    }
+  }
+
+  void SelectClassCases::set_fullname(const string& p_fullname)
+  {
+    Node::set_fullname(p_fullname);
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->set_fullname(p_fullname + ".scc_" + Int2string(i+1));
+    }
+  }
+
+  void SelectClassCases::set_my_sb(StatementBlock* p_sb, size_t p_index)
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->get_block()->set_my_sb(p_sb, p_index);
+    }
+  }
+
+  void SelectClassCases::set_my_def(Definition* p_def)
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->get_block()->set_my_def(p_def);
+    }
+  }
+
+  void SelectClassCases::set_my_ags(AltGuards *p_ags)
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->get_block()->set_my_ags(p_ags);
+    }
+  }
+
+  void SelectClassCases::set_my_laic_stmt(AltGuards* p_ags, Statement* p_loop_stmt)
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->get_block()->set_my_laic_stmt(p_ags, p_loop_stmt);
+    }
+  }
+
+  StatementBlock::returnstatus_t SelectClassCases::has_return() const
+  {
+    StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE;
+    bool has_else = false;
+    for (size_t i = 0; i < sccs.size(); i++) {
+      SelectClassCase* scc = sccs[i];
+      switch (scc->get_block()->has_return()) {
+      case StatementBlock::RS_NO:
+        if (ret_val == StatementBlock::RS_YES) {
+          return StatementBlock::RS_MAYBE;
+        }
+        else {
+          ret_val = StatementBlock::RS_NO;
+        }
+        break;
+      case StatementBlock::RS_YES:
+        if (ret_val == StatementBlock::RS_NO) {
+          return StatementBlock::RS_MAYBE;
+        }
+        else {
+          ret_val = StatementBlock::RS_YES;
+        }
+        break;
+      default:
+        return StatementBlock::RS_MAYBE;
+      }
+      if (scc->get_type() == NULL) {
+        has_else = true;
+        break;
+      }
+    }
+    if (!has_else && ret_val == StatementBlock::RS_YES) {
+      return StatementBlock::RS_MAYBE;
+    }
+    else {
+      return ret_val;
+    }
+  }
+
+  bool SelectClassCases::has_receiving_stmt() const
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      if (sccs[i]->get_block()->has_receiving_stmt()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void SelectClassCases::chk(ClassTypeBody* p_ref_class)
+  {
+    bool unreach = false;
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->chk(p_ref_class, unreach);
+    }
+  }
+
+  void SelectClassCases::chk_allowed_interleave()
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->get_block()->chk_allowed_interleave();
+    }
+  }
+
+  void SelectClassCases::set_code_section(GovernedSimple::code_section_t p_code_section)
+  {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->set_code_section(p_code_section);
+    }
+  }
+
+  char* SelectClassCases::generate_code(char* str, char*& def_glob_vars, char*& src_glob_vars,
+                                        const char* tmp_prefix, const char* ref_str)
+  {
+    bool unreach = false;
+    for (size_t i = 0; i < sccs.size(); i++) {
+      str = sccs[i]->generate_code_if(str, tmp_prefix, ref_str, i, unreach);
+      if (unreach) {
+        break;
+      }
+    }
+    if (!unreach) {
+      str = mputprintf(str, "goto %s_end;\n", tmp_prefix);
+    }
+    unreach = false;
+    for (size_t i = 0; i<sccs.size(); i++) {
+      str = sccs[i]->generate_code_stmt(str, def_glob_vars, src_glob_vars,
+        tmp_prefix, i, unreach);
+      if (unreach) {
+        break;
+      }
+    }
+    str = mputprintf(str, "%s_end: /* empty */;\n", tmp_prefix);
+    return str;
+  }
+
+  void SelectClassCases::ilt_generate_code(ILT* ilt, const char* tmp_prefix,
+                                           const char* ref_str)
+  {
+    char*& str = ilt->get_out_branches();
+    bool unreach = false;
+    for (size_t i = 0; i < sccs.size(); i++) {
+      if (unreach) {
+        break;
+      }
+      str = sccs[i]->generate_code_if(str, tmp_prefix, ref_str, i, unreach);
+    }
+    if (!unreach) {
+      str = mputprintf(str, "goto %s_end;\n", tmp_prefix);
+    }
+    unreach = false;
+    for (size_t i = 0; i < sccs.size(); i++) {
+      if (unreach) {
+        break;
+      }
+      sccs[i]->ilt_generate_code_stmt(ilt, tmp_prefix, i, unreach);
+    }
+    str = mputprintf(str, "%s_end:\n", tmp_prefix);
+  }
+
+  void SelectClassCases::set_parent_path(WithAttribPath* p_path) {
+    for (size_t i = 0; i < sccs.size(); i++) {
+      sccs[i]->set_parent_path(p_path);
+    }
+  }
 
   // =================================
   // ===== AltGuard
diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh
index e15f0abc292cef4cf93b1020dc78af54c14ad16f..65924659630b92d072aa12dbe48c91a218822d01 100644
--- a/compiler2/ttcn3/Statement.hh
+++ b/compiler2/ttcn3/Statement.hh
@@ -52,6 +52,8 @@ namespace Ttcn {
   class SelectCases;
   class SelectUnion;
   class SelectUnions;
+  class SelectClassCase;
+  class SelectClassCases;
   class AltGuard;
   class AltGuards;
   class WithAttribPath;
@@ -197,6 +199,7 @@ namespace Ttcn {
       S_IF, // if_stmt
       S_SELECT, // select
       S_SELECTUNION, // select union
+      S_SELECT_CLASS, // select class
       S_FOR, // loop
       S_WHILE, // loop
       S_DOWHILE, // loop
@@ -453,6 +456,11 @@ namespace Ttcn {
         Value *expr;
         SelectUnions *sus;
       } select_union;
+      
+      struct {
+        Reference* ref;
+        SelectClassCases* sccs;
+      } select_class;
 
       struct {
         Value *v;
@@ -539,6 +547,8 @@ namespace Ttcn {
     Statement(statementtype_t p_st, Value *p_expr, SelectCases *p_scs);
     /** Constructor used by S_SELECTUNION */
     Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus);
+    /** Constructor used by S_SELECT_CLASS */
+    Statement(statementtype_t p_st, Reference *p_ref, SelectClassCases *p_sccs);
     /** Constructor used by S_FOR */
     Statement(statementtype_t p_st, Definitions *p_defs, Assignment *p_ass,
               Value *p_final, Assignment *p_step, StatementBlock *p_block);
@@ -708,6 +718,7 @@ namespace Ttcn {
     void chk_if();
     void chk_select();
     void chk_select_union();
+    void chk_select_class();
     void chk_for();
     void chk_while();
     void chk_do_while();
@@ -848,6 +859,7 @@ namespace Ttcn {
     char *generate_code_if(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_select(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars);
+    char *generate_code_select_class(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_while(char *str, char*& def_glob_vars, char*& src_glob_vars);
     char *generate_code_dowhile(char *str, char*& def_glob_vars, char*& src_glob_vars);
@@ -862,6 +874,7 @@ namespace Ttcn {
     void ilt_generate_code_if(ILT *ilt);
     void ilt_generate_code_select(ILT *ilt);
     void ilt_generate_code_select_union(ILT *ilt);
+    void ilt_generate_code_select_class(ILT *ilt);
     void ilt_generate_code_call(ILT *ilt);
     void ilt_generate_code_for(ILT *ilt);
     void ilt_generate_code_while(ILT *ilt);
@@ -1567,6 +1580,89 @@ namespace Ttcn {
      */
     virtual void set_parent_path(WithAttribPath* p_path);
   };
+  
+  
+  /**
+   * Class to represent a select-class-case: the class type reference and
+   * the statement block.
+   */
+  class SelectClassCase : public Node, public Location {
+  private:
+    Common::Type* type;
+    StatementBlock* block;
+    Common::Scope* my_scope;
+    bool always_false;
+
+    SelectClassCase(const SelectCase& p);
+    SelectClassCase& operator=(const SelectCase& p);
+  public:
+    /** type == NULL means "else" case */
+    SelectClassCase(Common::Type* p_type, StatementBlock* p_block);
+    virtual ~SelectClassCase();
+    virtual SelectClassCase* clone() const;
+    virtual void set_my_scope(Scope* p_scope);
+    virtual void set_fullname(const string& p_fullname);
+    Common::Type* get_type() const { return type; }
+    StatementBlock* get_block() const { return block; }
+    /* checking functions */
+    void chk(ClassTypeBody* p_ref_class, bool& unreach);
+    /** Sets the code section selector of all embedded values and
+     *  templates to \a p_code_section. */
+    void set_code_section(GovernedSimple::code_section_t p_code_section);
+    char* generate_code_if(char* str, const char* tmp_prefix,
+      const char* ref_str, size_t idx, bool& unreach);
+    char* generate_code_stmt(char* str, char*& def_glob_vars, char*& src_glob_vars,
+      const char* tmp_prefix, size_t idx, bool& unreach);
+    void ilt_generate_code_stmt(ILT* ilt, const char* tmp_prefix,
+      size_t idx, bool& unreach);
+
+    /** Needed by implicit omit. Pushes attrib path down to definitions
+     */
+    virtual void set_parent_path(WithAttribPath* p_path);
+  };
+
+  /**
+   * Class to represent a select class construct.
+   */
+  class SelectClassCases : public Node {
+  private:
+    vector<SelectClassCase> sccs;
+
+    SelectClassCases(const SelectCases& p);
+    SelectClassCases& operator=(const SelectCases& p);
+  public:
+    SelectClassCases() : Node() { }
+    virtual ~SelectClassCases();
+    virtual SelectClassCases* clone() const;
+    void add_scc(SelectClassCase* p_scc);
+    size_t get_nof_sccs() const { return sccs.size(); }
+    SelectClassCase *get_scc_byIndex(size_t p_i) const { return sccs[p_i]; }
+    virtual void set_my_scope(Scope* p_scope);
+    virtual void set_fullname(const string& p_fullname);
+    void set_my_sb(StatementBlock* p_sb, size_t p_index);
+    void set_my_def(Definition* p_def);
+    void set_my_ags(AltGuards* p_ags);
+    void set_my_laic_stmt(AltGuards* p_ags, Statement* p_loop_stmt);
+    StatementBlock::returnstatus_t has_return() const;
+    bool has_receiving_stmt() const;
+    /* checking functions */
+    /** p_ref_class is the class of the select reference */
+    void chk(ClassTypeBody* p_ref_class);
+    /** checks whether all embedded statements are allowed in an interleaved
+     * construct */
+    void chk_allowed_interleave();
+    /** Sets the code section selector of all embedded values and
+     *  templates to \a p_code_section. */
+    void set_code_section(GovernedSimple::code_section_t p_code_section);
+    char* generate_code(char* str, char*& def_glob_vars, char*& src_glob_vars,
+      const char* tmp_prefix, const char* ref_str);   
+    void ilt_generate_code(ILT* ilt, const char* tmp_prefix, const char* ref_str);
+    
+
+    /** Needed by implicit omit. Pushes attrib path down to definitions
+     */
+    virtual void set_parent_path(WithAttribPath* p_path);
+  };
 
 
   /**
diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc
index bc17b0353b326014ab366172006b6c7585d8776c..6ed30376b9f2d567ae75455998277d7d61860325 100644
--- a/compiler2/ttcn3/Ttcnstuff.cc
+++ b/compiler2/ttcn3/Ttcnstuff.cc
@@ -3444,6 +3444,10 @@ namespace Ttcn {
                   "to that of inherited method `%s'",
                   local_id.get_dispname().c_str(), base_def->get_fullname().c_str());
               }
+              else if (base_func->is_final()) {
+                local_def->error("Cannot override final method `%s'",
+                  base_def->get_fullname().c_str());
+              }
               break; }
             default:
               local_def->error("%s shadows inherited member `%s'",
@@ -3649,6 +3653,12 @@ namespace Ttcn {
         "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,
+        "public:\n"
+        "static const char* class_name() { return \"%s\"; }\n\n",
+        class_id->get_dispname().c_str());
+      
       // members
       members->generate_code(target);
       if (default_constructor) {
diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l
index cc5de153a502a210be00068eb182d2694759d628..66a42b80e544200f6e7be17635f65c234c511f4e 100644
--- a/compiler2/ttcn3/compiler.l
+++ b/compiler2/ttcn3/compiler.l
@@ -1075,6 +1075,8 @@ NULL RETURN(NullValue);
   RETURN_NOLOCUPD(NE);
 }
 
+"=>" RETURN(ClassCastingSymbol);
+
 	/* Identifiers */
 
 {IDENTIFIER} {
diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y
index c100f7e74d3a1e78167d5dc0d47248cdc71b8e6c..578b5577aacdb89812c2f14f91ae571cd013cab1 100644
--- a/compiler2/ttcn3/compiler.y
+++ b/compiler2/ttcn3/compiler.y
@@ -186,6 +186,8 @@ static const string anyname("anytype");
   SelectCases *selectcases;
   SelectUnion *selectunion;
   SelectUnions *selectunions;
+  SelectClassCase* selectclasscase;
+  SelectClassCases* selectclasscases;
   SignatureExceptions *signexc;
   SignatureParam *signparam;
   SignatureParamList *signparamlist;
@@ -947,6 +949,7 @@ static const string anyname("anytype");
 %token SR ">>"
 %token RL "<@"
 %token _RR "@>" /* Name clash with bn.h:292 */
+%token ClassCastingSymbol "=>"
 
 /*********************************************************************
  * Semantic types of nonterminals
@@ -1024,6 +1027,8 @@ static const string anyname("anytype");
 %type <selectcases> seqSelectCase SelectCaseBody
 %type <selectunion> SelectUnion SelectunionElse
 %type <selectunions> seqSelectUnion SelectUnionBody
+%type <selectclasscase> SelectClassCase
+%type <selectclasscases> seqSelectClassCase SelectClassBody
 %type <signexc> ExceptionTypeList optExceptionSpec
 %type <signparam> SignatureFormalPar
 %type <signparamlist> SignatureFormalParList optSignatureFormalParList
@@ -1041,8 +1046,9 @@ static const string anyname("anytype");
   StartTimerStatement StopExecutionStatement StopStatement StopTCStatement
   StopTimerStatement TimeoutStatement TimerStatements TriggerStatement
   UnmapStatement VerdictStatements WhileStatement SelectCaseConstruct
-  SelectUnionConstruct UpdateStatement SetstateStatement SetencodeStatement
-  StopTestcaseStatement String2TtcnStatement ProfilerStatement int2enumStatement
+  SelectUnionConstruct SelectClassConstruct UpdateStatement SetstateStatement
+  SetencodeStatement StopTestcaseStatement String2TtcnStatement ProfilerStatement
+  int2enumStatement
 %type <statementblock> StatementBlock optElseClause FunctionStatementOrDefList
   ControlStatementOrDefList ModuleControlBody optFinallyDef
 %type <subtypeparse> ValueOrRange
@@ -1085,7 +1091,7 @@ AllOrTypeListWithTo TypeListWithFrom TypeListWithTo
   PredefinedValue ReadTimerOp ReferOp ReferencedValue RunningOp RunningTimerOp
   SelfOp SingleExpression SingleLowerBound SystemOp TemplateOps TimerOps
   TimerValue UpperBound Value ValueofOp VerdictOps VerdictValue optReplyValue
-  optTestcaseTimerValue optToClause ProfilerRunningOp
+  optTestcaseTimerValue optToClause ProfilerRunningOp OfClassOp ClassCastingOp
 %type <values> ArrayElementExpressionList seqArrayExpressionSpec
 %type <variableentries> VariableList
 %type <variableentry> VariableEntry
@@ -1239,6 +1245,7 @@ CatchStatement
 CharStringMatch
 CharStringValue
 CheckStatement
+ClassCastingOp
 ClassDef
 ClassMemberList
 ClearStatement
@@ -1357,6 +1364,7 @@ ObjIdComponentList
 ObjectIdentifierValue
 OctetStringMatch
 OctetStringValue
+OfClassOp
 OmitValue
 OpCall
 Ostring
@@ -1391,10 +1399,14 @@ RunningOp
 RunningTimerOp
 RunsOnSpec
 SUTStatements
+SelectClassBody
+SelectClassCase
+SelectClassConstruct
 SelfOp
 SendParameter
 SendStatement
 SenderSpec
+seqSelectClassCase
 SetDef
 SetencodeStatement
 SetLocalVerdict
@@ -3644,7 +3656,7 @@ ClassFunctionDef:
   optReturnType optError StatementBlock
   {
     $$ = new Def_Function($3, $4, $6, NULL, NULL, NULL, NULL, $8.type,
-      $8.returns_template, $8.template_restriction, $10);
+      $8.returns_template, $8.template_restriction, $2, $10);
     $$->set_location(infile, @$);
   }
 | FunctionKeyword optFinalModifier AbstractKeyword
@@ -3660,7 +3672,7 @@ ClassFunctionDef:
   optReturnType
   {
     $$ = new Def_ExtFunction($4, $5, $7, $9.type, $9.returns_template,
-      $9.template_restriction, true);
+      $9.template_restriction, $3, true);
     $$->set_location(infile, @$);
   }
 | FunctionKeyword optFinalModifier
@@ -3668,7 +3680,7 @@ ClassFunctionDef:
   optReturnType
   {
     $$ = new Def_ExtFunction($3, $4, $6, $8.type, $8.returns_template,
-      $8.template_restriction, false);
+      $8.template_restriction, $2, false);
     $$->set_location(infile, @$);
   }
 ;
@@ -4537,7 +4549,7 @@ FunctionDef: // 164
   {
     $5->set_location(infile, @4, @6);
     $$ = new Def_Function($2, $3, $5, $7, $8.mtcref, $8.systemref, $9, $10.type, $10.returns_template,
-                          $10.template_restriction, $12);
+                          $10.template_restriction, false, $12);
     $$->set_location(infile, @$);
   }
 ;
@@ -5643,7 +5655,7 @@ ExtFunctionDef: // 276
   {
     $6->set_location(infile, @5, @7);
     $$ = new Def_ExtFunction($3, $4, $6, $8.type, $8.returns_template,
-                             $8.template_restriction, true);
+                             $8.template_restriction, false, true);
     $$->set_location(infile, @$);
   }
 ;
@@ -9645,6 +9657,7 @@ BasicStatements: // 578
 | ConditionalConstruct { $$ = $1; }
 | SelectCaseConstruct { $$ = $1; }
 | SelectUnionConstruct { $$ = $1; }
+| SelectClassConstruct { $$ = $1; }
 ;
 
 Expression: // 579
@@ -10219,6 +10232,36 @@ OpCall: // 611
     $$ = new Value(Value::OPTYPE_NOW);
     $$->set_location(infile, @$);
   }
+| OfClassOp { $$ = $1; }
+| ClassCastingOp { $$ = $1; }
+;
+
+OfClassOp:
+  VariableRef OfKeyword ReferencedType
+  {
+    $$ = new Value(Value::OPTYPE_OF_CLASS, $3, $1);
+    $$->set_location(infile, @$);
+  }
+| VariableRef OfKeyword ObjectKeyword
+  {
+    Type* type = new Type(Type::T_CLASS);
+    type->set_location(infile, @3);
+    $$ = new Value(Value::OPTYPE_OF_CLASS, type, $1);
+    $$->set_location(infile, @$);
+  }
+;
+
+ClassCastingOp:
+  VariableRef ClassCastingSymbol ReferencedType
+  {
+    $$ = new Value(Value::OPTYPE_CLASS_CASTING, $3, $1);
+    $$->set_location(infile, @$);
+  }
+| VariableRef ClassCastingSymbol '(' VariableRef ')'
+  {
+    $$ = new Value(Value::OPTYPE_CLASS_CASTING_REF, $4, $1);
+    $$->set_location(infile, @$);
+  }
 ;
 
 PredefinedOps:
@@ -11091,6 +11134,53 @@ SelectunionElse:
     $$=new SelectUnion($3);  // The else branch, ids is empty
     $$->set_location(infile, @2);
   }
+;
+
+SelectClassConstruct:
+  SelectKeyword ClassKeyword '(' VariableRef ')' SelectClassBody
+  {
+    $$ = new Statement(Statement::S_SELECT_CLASS, $4, $6);
+    $$->set_location(infile, @$);
+  }
+;
+
+SelectClassBody:
+  '{' seqSelectClassCase optError '}' { $$ = $2; }
+| '{' error '}' { $$ = new SelectClassCases; }
+;
+
+seqSelectClassCase:
+  optError SelectClassCase
+  {
+    $$ = new SelectClassCases;
+    $$->add_scc($2);
+  }
+| seqSelectClassCase optError SelectClassCase
+  {
+    $$ = $1;
+    $$->add_scc($3);
+  }
+;
+
+SelectClassCase:
+  CaseKeyword '(' ReferencedType optError ')' StatementBlock optSemiColon
+  {
+    $$ = new SelectClassCase($3, $6);
+    $$->set_location(infile, @$);
+  }
+| CaseKeyword '(' ObjectKeyword optError ')' StatementBlock optSemiColon
+  {
+    Type* type = new Type(Type::T_CLASS);
+    type->set_location(infile, @3);
+    $$ = new SelectClassCase(type, $6);
+    $$->set_location(infile, @$);
+  }
+| CaseKeyword ElseKeyword StatementBlock optSemiColon
+  {
+    $$ = new SelectClassCase(NULL, $3);
+    $$->set_location(infile, @$);
+  }
+;
 
 
 
diff --git a/core/OOP.hh b/core/OOP.hh
index c72d9ef0acc6d50a3744abe86442d4bdd49d7133..5c215765273e07f5c139a623c072d242359585a7 100644
--- a/core/OOP.hh
+++ b/core/OOP.hh
@@ -50,6 +50,7 @@ public:
   virtual UNIVERSAL_CHARSTRING toString() {
     return UNIVERSAL_CHARSTRING("Object");
   }
+  static const char* class_name() { return "object"; }
 };
 
 // OBJECT_REF
@@ -191,6 +192,18 @@ public:
   boolean is_present() const {
     return ptr != NULL;
   }
+  
+  template<typename T2>
+  OBJECT_REF<T2> cast_to() const {
+    if (ptr == NULL) {
+      TTCN_error("Casting a null reference");
+    }
+    T2* new_ptr = dynamic_cast<T2*>(ptr);
+    if (new_ptr == NULL) {
+      TTCN_error("Invalid casting to class type `%s'", T2::class_name());
+    }
+    return OBJECT_REF<T2>(new_ptr);
+  }
 };
 
 template<typename T>
diff --git a/function_test/Semantic_Analyser/oop/oop_SE.ttcn b/function_test/Semantic_Analyser/oop/oop_SE.ttcn
index 063482daae89b416ab9a19ea041f7c0995fec02f..4eade8c7ea95a3f7daf1c71766feceb70ecd6fda 100644
--- a/function_test/Semantic_Analyser/oop/oop_SE.ttcn
+++ b/function_test/Semantic_Analyser/oop/oop_SE.ttcn
@@ -467,12 +467,49 @@ type class C47 {
   const integer m1 := 3;
   function m2() return boolean { return true; }
   function m3(in integer p) return charstring { return int2str(p); }
+  function @final m4() { }
 }
 
 type class C48 extends C47 { //^In type definition//
   var template octetstring m1; //template variable `@oop_SE.C48.m1' shadows inherited member `@oop_SE.C47.m1'//
   var integer m2; //variable `@oop_SE.C48.m2' shadows inherited method `@oop_SE.C47.m2'//
   function m3() return charstring { return "1"; } //The prototype of method `m3' is not identical to that of inherited method `@oop_SE.C47.m3'//
+  function m4() { } //Cannot override final method `@oop_SE.C47.m4'//
+}
+
+
+function f_of_operator() { //^In function definition//
+  var C0 x := C0.create;
+  if (x of object) { log(1); }
+  if (x of C0) { log(2); }
+  if (x of C5) { log(3); } //^In if statement// //Control never reaches this code because the conditional expression evaluates to false//
+  if (x of Comp) { log(4); } //^In if statement// //^In the second operand of operation `of'// //Class type was expected//
+  if (C0 of C0) { log(5); } //^In if statement// //^In the first operand of operation `of'// //Reference to a variable or value parameter was expected instead of type `@oop_SE.C0'//
+}
+
+function f_select_class() { //^In function definition//
+  var C0 x := C0.create;
+  select class (x) { //^In select class statement//
+    case (C0) { log(1); }
+    case (C5) { log(2); } //^In select class case statement// //Control never reaches this code because the case will never be chosen//
+    case else { log(3); }
+    case (Comp) { log(4); } //^In select class case statement// //Control never reaches this code because of previous effective case\(s\)// //Class type was expected//
+  }
+  var integer y;
+  select class (y) { //^In select class statement// //Reference to a class object was expected//
+    case (object) { log(5); }
+    case else { log(6); }
+  }
+}
+
+function f_casting() { //^In function definition//
+  var C35 x1 := C36.create;
+  var C36 x2 := x1 => C36;
+  var C0 x3 := x1 => C0; //^In variable definition// //^In the second operand of operation `=>'// //Cannot cast an object of class type `@oop_SE.C35' to class type `@oop_SE.C0'//
+  var C35 x4 := x2 => (x1); //^In variable definition// //^In the second operand of operation `=>'// //Cannot cast to abstract class type `@oop_SE.C35'//
+  var C36 x5 := c => C36; //^In variable definition// //^In the first operand of operation `=>'// //Reference to a variable or value parameter was expected instead of constant `@oop_SE.c'//
+  var Rec x6 := x1 => Rec; //^In variable definition// //^In the second operand of operation `=>'// //Class type was expected//
+  var integer x7 := x1 => (x6); //^In variable definition// //^In the second operand of operation `=>'// //Class type was expected//
 }
 
 
diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn
index 6e0d2d17c19190a691cc45f3f1f86bcbf16ebc23..0c088dcba7bbba1aaa40832ee98ec2ba8c598a25 100644
--- a/regression_test/oop/oop.ttcn
+++ b/regression_test/oop/oop.ttcn
@@ -512,6 +512,157 @@ testcase tc_destructor() runs on CT {
   var Destructor x := Destructor.create;
 }
 
+testcase tc_of_operator() runs on CT {
+  var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0);
+  var SubClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *);
+  var object v_obj := Node.create(3, null);
+  var BaseClass v_null := null;
+  
+  if (not v_sub of BaseClass) {
+    setverdict(fail, "#1");
+  }
+  if (not v_sub of SubClass) {
+    setverdict(fail, "#2");
+  }
+  if (v_sub of FinalClass) {
+    setverdict(fail, "#3");
+  }
+  if (not v_final of BaseClass) {
+    setverdict(fail, "#4");
+  }
+  if (not v_final of SubClass) {
+    setverdict(fail, "#5");
+  }
+  if (not v_final of FinalClass) {
+    setverdict(fail, "#6");
+  }
+  if (v_sub of Node) {
+    setverdict(fail, "#7");
+  }
+  if (not v_sub of object) {
+    setverdict(fail, "#8");
+  }
+  if (not v_obj of object) {
+    setverdict(fail, "#9");
+  }
+  if (not v_obj of Node) {
+    setverdict(fail, "#10");
+  }
+  if (v_obj of AbstractClass) {
+    setverdict(fail, "#11");
+  }
+  if (v_null of BaseClass) {
+    setverdict(fail, "#12");
+  }
+  if (v_null of object) {
+    setverdict(fail, "#13");
+  }
+  setverdict(pass);
+}
+
+testcase tc_select_class() runs on CT {
+  var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0);
+  var SubClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *);
+  var object v_obj := Node.create(3, null);
+  var BaseClass v_null := null;
+  
+  select class (v_sub) {
+    case (FinalClass) {
+      setverdict(fail, "#1 FinalClass");
+    }
+    case (SubClass) {
+      setverdict(pass);
+    }
+    case (BaseClass) {
+      setverdict(fail, "#1 BaseClass");
+    }
+  }
+  
+  select class (v_final) {
+    case (FinalClass) {
+      setverdict(pass);
+    }
+    case (SubClass) {
+      setverdict(fail, "#2 SubClass");
+    }
+    case (BaseClass) {
+      setverdict(fail, "#2 BaseClass");
+    }
+  }
+  
+  select class (v_obj) {
+    case (object) {
+      setverdict(pass);
+    }
+    case (Node) {
+      setverdict(fail, "#3 Node");
+    }
+    case else {
+      setverdict(fail, "#3 else");
+    }
+  }
+  
+  select class (v_null) {
+    case (BaseClass) {
+      setverdict(fail, "#4 BaseClass");
+    }
+    case (Node) {
+      setverdict(fail, "#4 Node");
+    }
+    case (object) {
+      setverdict(fail, "#4 object");
+    }
+    case else {
+      setverdict(pass);
+    }
+  }
+}
+
+testcase tc_casting() runs on CT {
+  var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0);
+  var SubClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *);
+  var object v_obj := Node.create(3, null);
+  var BaseClass v_null := null;
+  var Node v_null2 := null;
+  
+  var BaseClass v_base := v_sub => BaseClass;
+  var BaseClass v_base2 := v_sub;
+  if (log2str(v_base) != log2str(v_base2)) {
+    setverdict(fail, "#1: ", v_base);
+  }
+  var FinalClass v_final2 := v_final => FinalClass;
+  if (log2str(v_final2) != log2str(v_final)) {
+    setverdict(fail, "#2: ", v_final2);
+  }
+  var Node v_node := v_obj => (v_null2);
+  if (log2str(v_node) != log2str(v_obj)) {
+    setverdict(fail, "#3: ", v_node);
+  }
+  var SubClass v_sub2 := v_sub => SubClass;
+  if (log2str(v_sub2) != log2str(v_sub)) {
+    setverdict(fail, "#4: ", v_sub2);
+  }
+  @try {
+    var SubClass v_bad := v_null => SubClass;
+    setverdict(fail, "#5: Error expected");
+  }
+  @catch(msg) {
+    if (not match(msg, pattern "*Casting a null reference")) {
+      setverdict(fail, "#5: Invalid error: ", msg);
+    }
+  }
+  @try {
+    var FinalClass v_bad2 := v_sub => FinalClass;
+    setverdict(fail, "#6: Error expected");
+  }
+  @catch(msg) {
+    if (not match(msg, pattern "*Invalid casting to class type `FinalClass'")) {
+      setverdict(fail, "#6: Invalid error: ", msg);
+    }
+  }
+  setverdict(pass);
+}
+
 
 control {
   execute(tc_members_and_methods());
@@ -525,6 +676,9 @@ control {
   execute(tc_order());
   execute(tc_abstract());
   execute(tc_destructor());
+  execute(tc_of_operator());
+  execute(tc_select_class());
+  execute(tc_casting());
 }
 
 }