From d2c608fc1d502911105fec8e5b5091a06a762031 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20H=C3=BCbinette=20=28Hubbe=29?= <hubbe@hubbe.net>
Date: Thu, 7 Nov 1996 20:57:40 -0800
Subject: [PATCH] object(foo), prog(), 'constant' and class foo {} implemented

Rev: src/ChangeLog:1.18
Rev: src/builtin_functions.c:1.9
Rev: src/builtin_functions.h:1.2
Rev: src/docode.c:1.3
Rev: src/interpret.c:1.8
Rev: src/language.yacc:1.6
Rev: src/lex.c:1.6
Rev: src/object.c:1.3
Rev: src/object.h:1.3
Rev: src/pike_types.c:1.5
Rev: src/program.c:1.4
Rev: src/program.h:1.2
Rev: src/testsuite.in:1.6
---
 src/ChangeLog           |   8 +++
 src/builtin_functions.c |  17 +----
 src/builtin_functions.h |   1 -
 src/docode.c            |   8 +--
 src/interpret.c         |  45 +++++-------
 src/language.yacc       | 152 +++++++++++++++++++++++++++++++++-------
 src/lex.c               |   1 +
 src/object.c            |  74 +++++++++++--------
 src/object.h            |   3 +
 src/pike_types.c        |  53 ++++++++++++--
 src/program.c           |  66 +++++++++++++++--
 src/program.h           |   8 +++
 src/testsuite.in        |  14 ++++
 13 files changed, 339 insertions(+), 111 deletions(-)

diff --git a/src/ChangeLog b/src/ChangeLog
index d36296972d..499b87c9f6 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,11 @@
+Thu Nov  7 20:49:42 1996  Fredrik Hubinette  <hubbe@tymin.signum.se>
+
+	* 'constant foo=<value>;' implemented on top level
+	* prog(); works now works like clone(prog)
+	* clone removed
+	* object(const) or object("prog") can now be used
+	* class foo {}; now works like constant foo = class {};
+
 Sat Nov  2 22:17:45 1996  Fredrik Hubinette  <hubbe@tymin.blarg.com>
 
 	* language.yacc: if(int a) implemente
diff --git a/src/builtin_functions.c b/src/builtin_functions.c
index 0ab620cb11..6dc24feb91 100644
--- a/src/builtin_functions.c
+++ b/src/builtin_functions.c
@@ -262,21 +262,6 @@ void f_search(INT32 args)
   }
 }
 
-void f_clone(INT32 args)
-{
-  struct object *o;
-
-  if(args<1)
-    error("Too few arguments to clone.\n");
-
-  if(sp[-args].type != T_PROGRAM)
-    error("Bad argument 1 to clone.\n");
-
-  o=clone(sp[-args].u.program,args-1);
-  pop_stack();
-  push_object(o);
-}
-
 void f_call_function(INT32 args)
 {
   INT32 expected_stack=sp-args+2-evaluator_stack;
@@ -1454,7 +1439,7 @@ void init_builtin_efuns()
   add_efun("arrayp",  f_arrayp,  "function(mixed:int)",0);
   add_efun("backtrace",f_backtrace,"function(:array(array(function|int|string)))",OPT_EXTERNAL_DEPEND);
   add_efun("call_function",f_call_function,"function(mixed,mixed ...:mixed)",OPT_SIDE_EFFECT | OPT_EXTERNAL_DEPEND);
-  add_efun("clone",f_clone,"function(program,mixed...:object)",OPT_EXTERNAL_DEPEND);
+
   add_efun("column",f_column,"function(array,mixed:array)",0);
   add_efun("combine_path",f_combine_path,"function(string,string:string)",0);
   add_efun("compile_file",f_compile_file,"function(string:program)",OPT_EXTERNAL_DEPEND);
diff --git a/src/builtin_functions.h b/src/builtin_functions.h
index 5ef0f11cb0..d9ac9d8273 100644
--- a/src/builtin_functions.h
+++ b/src/builtin_functions.h
@@ -23,7 +23,6 @@ void f_random(INT32 args);
 void f_random_seed(INT32 args);
 void f_query_num_arg(INT32 args);
 void f_search(INT32 args);
-void f_clone(INT32 args);
 void f_call_function(INT32 args);
 void f_backtrace(INT32 args);
 void f_add_constant(INT32 args);
diff --git a/src/docode.c b/src/docode.c
index cc83acaf0f..7e34f30292 100644
--- a/src/docode.c
+++ b/src/docode.c
@@ -424,9 +424,9 @@ static int do_docode2(node *n,int flags)
 	break;
 
       case F_IDENTIFIER:
-	if(ID_FROM_INT(& fake_program, CDR(n)->u.number)->flags & IDENTIFIER_FUNCTION)
+	if(!IDENTIFIER_IS_VARIABLE( ID_FROM_INT(& fake_program, CDR(n)->u.number)->flags))
 	{
-	  yyerror("Cannot assign functions.\n");
+	  yyerror("Cannot assign functions or constants.\n");
 	}else{
 	  if(do_docode(CAR(n),0)!=1) yyerror("RHS is void!");
 	  emit(flags & DO_POP ? F_ASSIGN_GLOBAL_AND_POP:F_ASSIGN_GLOBAL,
@@ -677,7 +677,7 @@ static int do_docode2(node *n,int flags)
       return 1;
     }
     else if(CAR(n)->token == F_IDENTIFIER &&
-	    ID_FROM_INT(& fake_program, CAR(n)->u.number)->flags & IDENTIFIER_FUNCTION)
+	    IDENTIFIER_IS_FUNCTION(ID_FROM_INT(& fake_program, CAR(n)->u.number)->flags))
     {
       emit2(F_MARK);
       do_docode(CDR(n),0);
@@ -992,7 +992,7 @@ static int do_docode2(node *n,int flags)
     }
 
   case F_IDENTIFIER:
-    if(ID_FROM_INT(& fake_program, n->u.number)->flags & IDENTIFIER_FUNCTION)
+    if(IDENTIFIER_IS_FUNCTION(ID_FROM_INT(& fake_program, n->u.number)->flags))
     {
       if(flags & DO_LVALUE)
       {
diff --git a/src/interpret.c b/src/interpret.c
index 9ab9009051..2e55742b20 100644
--- a/src/interpret.c
+++ b/src/interpret.c
@@ -568,32 +568,13 @@ static void eval_instruction(unsigned char *pc)
       break;
 
       /* The not so basic 'push value' instructions */
-      CASE(F_GLOBAL)
-      {
-	struct identifier *i;
-	INT32 tmp=GET_ARG() + fp->context.identifier_level;
-
-	if(!fp->current_object->prog)
-	  error("Cannot access global variables in destructed object.\n");
-
-	i=ID_FROM_INT(fp->current_object->prog, tmp);
-	if(i->run_time_type == T_MIXED)
-	{
-	  struct svalue *s;
-	  s=(struct svalue *)GLOBAL_FROM_INT(tmp);
-	  check_destructed(s);
-	  assign_svalue_no_free(sp,s);
-	}else{
-	  union anything *u;
-	  u=(union anything *)GLOBAL_FROM_INT(tmp);
-	  check_short_destructed(u,i->run_time_type);
-	  
-	  assign_from_short_svalue_no_free(sp,u, i->run_time_type);
-	}
-	sp++;
-	print_return_value();
-	break;
-      }
+      CASE(F_GLOBAL);
+      low_object_index_no_free(sp,
+			       fp->current_object,
+			       GET_ARG() + fp->context.identifier_level);
+      sp++;
+      print_return_value();
+      break;
 
       CASE(F_LOCAL);
       assign_svalue_no_free(sp++,fp->locals+GET_ARG());
@@ -656,7 +637,6 @@ static void eval_instruction(unsigned char *pc)
       fp->locals[instr].u.integer--;
       break;
 
-
       CASE(F_LTOSVAL);
       lvalue_to_svalue_no_free(sp,sp-2);
       sp++;
@@ -692,6 +672,10 @@ static void eval_instruction(unsigned char *pc)
 	  error("Cannot access global variables in destructed object.\n");
 
 	i=ID_FROM_INT(fp->current_object->prog, tmp);
+
+	if(!IDENTIFIER_IS_VARIABLE(i->flags))
+	  error("Cannot re-assign functions or constants.\n");
+
 	if(i->run_time_type == T_MIXED)
 	{
 	  sp[0].type=T_LVALUE;
@@ -1461,6 +1445,13 @@ void strict_apply_svalue(struct svalue *s, INT32 args)
     apply_array(s->u.array,args);
     break;
 
+  case T_PROGRAM:
+    {
+      struct object *o=clone(s->u.program,args);
+      push_object(o);
+    }
+    break;
+
   default:
     error("Call to non-function value.\n");
   }
diff --git a/src/language.yacc b/src/language.yacc
index 5a1e2d157f..6098477d68 100644
--- a/src/language.yacc
+++ b/src/language.yacc
@@ -80,6 +80,7 @@
 %token F_CLASS
 %token F_COLON_COLON
 %token F_COMMA
+%token F_CONSTANT
 %token F_CONTINUE 
 %token F_DEFAULT
 %token F_DIV_EQ
@@ -299,6 +300,7 @@ void fix_comp_stack(int sp)
 %type <n> for_expr
 %type <n> foreach
 %type <n> gauge
+%type <n> idents
 %type <n> lambda
 %type <n> local_name_list
 %type <n> lvalue
@@ -341,13 +343,99 @@ string_constant: low_string
 optional_rename_inherit: ':' F_IDENTIFIER { $$=$2; }
   | { $$=0; }
   ;
+
+program_ref: string_constant
+  {
+    reference_shared_string($1);
+    push_string($1);
+    push_string($1);
+    reference_shared_string(current_file);
+    push_string(current_file);
+    SAFE_APPLY_MASTER("handle_inherit", 2);
+
+    if(sp[-1].type != T_PROGRAM)
+      my_yyerror("Couldn't cast program to string (%s)",$1->str);
+  }
+  | idents
+  {
+    push_string(make_shared_string(""));
+    switch($1->token)
+    {
+    case F_CONSTANT:
+      if($1->u.sval.type == T_PROGRAM)
+      {
+	push_svalue(& $1->u.sval);
+      }else{
+	yyerror("Illegal program identifier");
+	push_int(0);
+      }
+      break;
+
+    case F_IDENTIFIER:
+      {
+	struct identifier *i;
+	setup_fake_program();
+	i=ID_FROM_INT(& fake_program, $1->u.number);
+
+	if(IDENTIFIER_IS_CONSTANT(i->flags))
+	{
+	  push_svalue(PROG_FROM_INT(&fake_program, $1->u.number)->constants +
+		      i->func.offset);
+	}else{
+	  yyerror("Illegal program identifier");
+	  push_int(0);
+	}
+	break;
+      }
+    }
+    free_node($1);
+  }
+  ;
           
-inheritance: modifiers F_INHERIT string_constant optional_rename_inherit ';'
+inheritance: modifiers F_INHERIT program_ref optional_rename_inherit ';'
   {
-    simple_do_inherit($3,$1,$4);
+    struct pike_string *s;
+    if(sp[-1].type == T_PROGRAM)
+    {
+      s=sp[-2].u.string;
+      if($4) s=$4;
+      do_inherit(sp[-1].u.program,$1,s);
+      if($4) free_string($4);
+    }
+    pop_n_elems(2);
   }
   ;
 
+constant_name: F_IDENTIFIER '=' expr0
+  {
+    int tmp;
+    /* This can be made more lenient in the future */
+    if(!is_const($3))
+    {
+      struct svalue tmp;
+      yyerror("Constant definition is not constant.");
+      tmp.type=T_INT;
+      tmp.u.integer=0;
+      add_constant($1,&tmp, current_modifiers);
+    } else {
+      tmp=eval_low($3);
+      if(tmp < 1)
+	yyerror("Error in constant definition.");
+      pop_n_elems(tmp-1);
+      add_constant($1,sp-1,current_modifiers);
+      free_string($1);
+      pop_stack();
+    }
+  }
+  ;
+
+constant_list: constant_name
+  | constant_list ',' constant_name
+  ;
+
+constant: F_CONSTANT modifiers constant_list ';'
+  ;
+
 block_or_semi: block
   {
     $$ = mknode(F_ARG_LIST,$1,mknode(F_RETURN,mkintnode(0),0));
@@ -458,6 +546,8 @@ def: modifiers type_or_error optional_stars F_IDENTIFIER '(' arguments ')'
   }
   | modifiers type_or_error name_list ';' {}
   | inheritance {}
+  | constant {}
+  | class {}
   | error 
   {
     reset_type_stack();
@@ -471,7 +561,7 @@ optional_dot_dot_dot: F_DOT_DOT_DOT { $$=1; }
   ;
 
 optional_identifier: F_IDENTIFIER
-  | /* empty */ { $$=make_shared_string(""); }
+  | /* empty */ { $$=0; }
   ;
 
 
@@ -484,6 +574,7 @@ new_arg_name: type optional_dot_dot_dot optional_identifier
       push_type(T_ARRAY);
       varargs=1;
     }
+    if(!$3) $3=make_shared_string("");
     if(islocal($3) >= 0)
       my_yyerror("Variable '%s' appear twice in argument list.",
 		 $3->str);
@@ -534,16 +625,25 @@ type2: type2 '|' type3 { push_type(T_OR); }
 type3: F_INT_ID      { push_type(T_INT); }
   | F_FLOAT_ID    { push_type(T_FLOAT); }
   | F_STRING_ID   { push_type(T_STRING); }
-  | F_OBJECT_ID   { push_type(T_OBJECT); }
   | F_PROGRAM_ID  { push_type(T_PROGRAM); }
   | F_VOID_ID     { push_type(T_VOID); }
   | F_MIXED_ID    { push_type(T_MIXED); }
+  | F_OBJECT_ID   opt_object_type { push_type(T_OBJECT); }
   | F_MAPPING_ID opt_mapping_type { push_type(T_MAPPING); }
   | F_ARRAY_ID opt_array_type { push_type(T_ARRAY); }
   | F_MULTISET_ID opt_array_type { push_type(T_MULTISET); }
   | F_FUNCTION_ID opt_function_type { push_type(T_FUNCTION); }
   ;
 
+opt_object_type:  /* Empty */ { push_type_int(0); }
+  | '(' program_ref ')'
+  {
+    if(sp[-1].type == T_PROGRAM)
+      push_type_int(sp[-1].u.program->id);
+    pop_n_elems(2);
+  }
+  ;
+
 opt_function_type: '('
   {
     type_stack_mark();
@@ -786,7 +886,7 @@ lambda: F_LAMBDA
   }
   ;
 
-class: F_CLASS '{'
+class: F_CLASS optional_identifier '{'
   {
     start_new_program();
   }
@@ -803,6 +903,7 @@ class: F_CLASS '{'
       s.type=T_PROGRAM;
       s.subtype=0;
     }
+    if($2) add_constant($2, &s, 0);
     $$=mksvaluenode(&s);
     free_svalue(&s);
   }
@@ -1007,7 +1108,28 @@ expr4: string
   | sscanf
   | lambda
   | class
-  | F_IDENTIFIER
+  | idents
+  | expr4 '(' expr_list ')' { $$=mkapplynode($1,$3); }
+  | expr4 '[' expr0 ']' { $$=mknode(F_INDEX,$1,$3); }
+  | expr4 '['  comma_expr_or_zero F_DOT_DOT comma_expr_or_maxint ']'
+  {
+    $$=mknode(F_RANGE,$1,mknode(F_ARG_LIST,$3,$5));
+  }
+  | '(' comma_expr2 ')' { $$=$2; }
+  | '(' '{' expr_list '}' ')'
+    { $$=mkefuncallnode("aggregate",$3); }
+  | '(' '[' m_expr_list ']' ')'
+    { $$=mkefuncallnode("aggregate_mapping",$3); };
+  | F_MULTISET_START expr_list F_MULTISET_END
+    { $$=mkefuncallnode("aggregate_multiset",$2); }
+  | expr4 F_ARROW F_IDENTIFIER
+  {
+    $$=mknode(F_INDEX,$1,mkstrnode($3));
+    free_string($3);
+  }
+  ;
+
+idents: F_IDENTIFIER
   {
     int i;
     struct efun *f;
@@ -1037,24 +1159,6 @@ expr4: string
     }
     free_string($3);
   }
-  | expr4 '(' expr_list ')' { $$=mkapplynode($1,$3); }
-  | expr4 '[' expr0 ']' { $$=mknode(F_INDEX,$1,$3); }
-  | expr4 '['  comma_expr_or_zero F_DOT_DOT comma_expr_or_maxint ']'
-  {
-    $$=mknode(F_RANGE,$1,mknode(F_ARG_LIST,$3,$5));
-  }
-  | '(' comma_expr2 ')' { $$=$2; }
-  | '(' '{' expr_list '}' ')'
-    { $$=mkefuncallnode("aggregate",$3); }
-  | '(' '[' m_expr_list ']' ')'
-    { $$=mkefuncallnode("aggregate_mapping",$3); };
-  | F_MULTISET_START expr_list F_MULTISET_END
-    { $$=mkefuncallnode("aggregate_multiset",$2); }
-  | expr4 F_ARROW F_IDENTIFIER
-  {
-    $$=mknode(F_INDEX,$1,mkstrnode($3));
-    free_string($3);
-  }
   | F_IDENTIFIER F_COLON_COLON F_IDENTIFIER
   {
     int f;
diff --git a/src/lex.c b/src/lex.c
index 6967475416..9e2116db0d 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -101,6 +101,7 @@ struct keyword reserved_words[] =
 { "catch",	F_CATCH, },
 { "class",	F_CLASS, },
 { "continue",	F_CONTINUE, },
+{ "constant",	F_CONSTANT, },
 { "default",	F_DEFAULT, },
 { "do",		F_DO, },
 { "predef",	F_PREDEF, },
diff --git a/src/object.c b/src/object.c
index 7f39e727ca..8f4f163879 100644
--- a/src/object.c
+++ b/src/object.c
@@ -70,7 +70,7 @@ struct object *clone(struct program *p, int args)
 
     for(d=0;d<(int)frame.context.prog->num_identifiers;d++)
     {
-      if(frame.context.prog->identifiers[d].flags & IDENTIFIER_FUNCTION) 
+      if(!IDENTIFIER_IS_VARIABLE(frame.context.prog->identifiers[d].flags))
 	continue;
       
       if(frame.context.prog->identifiers[d].run_time_type == T_MIXED)
@@ -200,7 +200,7 @@ void destruct(struct object *o)
 
     for(d=0;d<(int)frame.context.prog->num_identifiers;d++)
     {
-      if(frame.context.prog->identifiers[d].flags & IDENTIFIER_FUNCTION) 
+      if(!IDENTIFIER_IS_VARIABLE(frame.context.prog->identifiers[d].flags)) 
 	continue;
       
       if(frame.context.prog->identifiers[d].run_time_type == T_MIXED)
@@ -298,35 +298,53 @@ void really_free_object(struct object *o)
 }
 
 
-static void low_object_index_no_free(struct svalue *to,
-				     struct object *o,
-				     INT32 f)
+void low_object_index_no_free(struct svalue *to,
+			      struct object *o,
+			      INT32 f)
 {
   struct identifier *i;
   struct program *p=o->prog;
+  
+  if(!p)
+    error("Cannot access global variables in destructed object.\n");
 
   i=ID_FROM_INT(p, f);
 
-  if(i->flags & IDENTIFIER_FUNCTION)
+  switch(i->flags & (IDENTIFIER_FUNCTION | IDENTIFIER_CONSTANT))
   {
+  case IDENTIFIER_FUNCTION:
+  case IDENTIFIER_C_FUNCTION:
+  case IDENTIFIER_PIKE_FUNCTION:
     to->type=T_FUNCTION;
     to->subtype=f;
     to->u.object=o;
     o->refs++;
-  }
-  else if(i->run_time_type == T_MIXED)
-  {
-    struct svalue *s;
-    s=(struct svalue *)LOW_GET_GLOBAL(o,f,i);
-    check_destructed(s);
-    assign_svalue_no_free(to, s);
-  }
-  else
-  {
-    union anything *u;
-    u=(union anything *)LOW_GET_GLOBAL(o,f,i);
-    check_short_destructed(u,i->run_time_type);
-    assign_from_short_svalue_no_free(to, u, i->run_time_type);
+    break;
+
+  case IDENTIFIER_CONSTANT:
+    {
+      struct svalue *s;
+      s=PROG_FROM_INT(p,f)->constants + i->func.offset;
+      check_destructed(s);
+      assign_svalue_no_free(to, s);
+      break;
+    }
+
+  case 0:
+    if(i->run_time_type == T_MIXED)
+    {
+      struct svalue *s;
+      s=(struct svalue *)LOW_GET_GLOBAL(o,f,i);
+      check_destructed(s);
+      assign_svalue_no_free(to, s);
+    }
+    else
+    {
+      union anything *u;
+      u=(union anything *)LOW_GET_GLOBAL(o,f,i);
+      check_short_destructed(u,i->run_time_type);
+      assign_from_short_svalue_no_free(to, u, i->run_time_type);
+    }
   }
 }
 
@@ -399,9 +417,9 @@ static void object_low_set_index(struct object *o,
 
   i=ID_FROM_INT(p, f);
 
-  if(i->flags & IDENTIFIER_FUNCTION)
+  if(!IDENTIFIER_IS_VARIABLE(i->flags))
   {
-    error("Cannot assign functions.\n");
+    error("Cannot assign functions or constants.\n");
   }
   else if(i->run_time_type == T_MIXED)
   {
@@ -479,9 +497,9 @@ static union anything *object_low_get_item_ptr(struct object *o,
 
   i=ID_FROM_INT(p, f);
 
-  if(i->flags & IDENTIFIER_FUNCTION)
+  if(!IDENTIFIER_IS_VARIABLE(i->flags))
   {
-    error("Cannot assign functions.\n");
+    error("Cannot assign functions or constants.\n");
   }
   else if(i->run_time_type == T_MIXED)
   {
@@ -567,7 +585,7 @@ void verify_all_objects()
       {
 	struct identifier *i;
 	i=ID_FROM_INT(o->prog, e);
-	if(i->flags & IDENTIFIER_FUNCTION)
+	if(!IDENTIFIER_IS_VARIABLE(i->flags))
 	  continue;
 
 	if(i->run_time_type == T_MIXED)
@@ -625,7 +643,7 @@ int object_equal_p(struct object *a, struct object *b, struct processing *p)
     {
       struct identifier *i;
       i=ID_FROM_INT(a->prog, e);
-      if(i->flags & IDENTIFIER_FUNCTION)
+      if(!IDENTIFIER_IS_VARIABLE(i->flags))
 	continue;
 
       if(i->run_time_type == T_MIXED)
@@ -718,7 +736,7 @@ void gc_mark_object_as_referenced(struct object *o)
 	
 	i=ID_FROM_INT(o->prog, e);
 	
-	if(i->flags & IDENTIFIER_FUNCTION) continue;
+	if(!IDENTIFIER_IS_VARIABLE(i->flags)) continue;
 	
 	if(i->run_time_type == T_MIXED)
 	{
@@ -747,7 +765,7 @@ void gc_check_all_objects()
 	
 	i=ID_FROM_INT(o->prog, e);
 	
-	if(i->flags & IDENTIFIER_FUNCTION) continue;
+	if(!IDENTIFIER_IS_VARIABLE(i->flags)) continue;
 	
 	if(i->run_time_type == T_MIXED)
 	{
diff --git a/src/object.h b/src/object.h
index 90d7d60a70..9abde9f91c 100644
--- a/src/object.h
+++ b/src/object.h
@@ -40,6 +40,9 @@ struct object *master();
 void destruct(struct object *o);
 void destruct_objects_to_destruct();
 void really_free_object(struct object *o);
+void low_object_index_no_free(struct svalue *to,
+			      struct object *o,
+			      INT32 f);
 void object_index_no_free(struct svalue *to,
 			  struct object *o,
 			  struct svalue *index);
diff --git a/src/pike_types.c b/src/pike_types.c
index bbc4a74323..cbfcee4a0d 100644
--- a/src/pike_types.c
+++ b/src/pike_types.c
@@ -93,12 +93,16 @@ static int type_length(char *t)
   case T_INT:
   case T_FLOAT:
   case T_STRING:
-  case T_OBJECT:
   case T_PROGRAM:
   case T_MIXED:
   case T_VOID:
   case T_UNKNOWN:
     break;
+
+
+  case T_OBJECT:
+    t+=sizeof(INT32);
+    break;
   }
   return t-q;
 }
@@ -161,6 +165,17 @@ void push_type(unsigned char tmp)
     yyerror("Type stack overflow.");
 }
 
+void push_type_int(unsigned INT32 i)
+{
+  if(type_stackp + sizeof(i)> type_stack + sizeof(type_stack))
+    yyerror("Type stack overflow.");
+
+  type_stack_mark();
+  MEMCPY(type_stackp, &i, sizeof(i));
+  type_stackp+=sizeof(i);
+  type_stack_reverse();
+}
+
 void push_unfinished_type(char *s)
 {
   int e;
@@ -217,7 +232,11 @@ static void internal_parse_typeA(char **_s)
   
   if(!strcmp(buf,"int")) push_type(T_INT);
   else if(!strcmp(buf,"float")) push_type(T_FLOAT);
-  else if(!strcmp(buf,"object")) push_type(T_OBJECT);
+  else if(!strcmp(buf,"object"))
+  {
+    push_type_int(0);
+    push_type(T_OBJECT);
+  }
   else if(!strcmp(buf,"program")) push_type(T_PROGRAM);
   else if(!strcmp(buf,"string")) push_type(T_STRING);
   else if(!strcmp(buf,"void")) push_type(T_VOID);
@@ -439,7 +458,10 @@ void stupid_describe_type(char *a,INT32 len)
     case T_FLOAT: printf("float"); break;
     case T_STRING: printf("string"); break;
     case T_PROGRAM: printf("program"); break;
-    case T_OBJECT: printf("object"); break;
+    case T_OBJECT:
+      printf("object(%ld)",(long)EXTRACT_INT(a+e+1));
+      e+=sizeof(INT32);
+      break;
     case T_FUNCTION: printf("function"); break;
     case T_ARRAY: printf("array"); break;
     case T_MAPPING: printf("mapping"); break;
@@ -475,7 +497,10 @@ char *low_describe_type(char *t)
   case T_INT: my_strcat("int"); break;
   case T_FLOAT: my_strcat("float"); break;
   case T_PROGRAM: my_strcat("program"); break;
-  case T_OBJECT: my_strcat("object"); break;
+  case T_OBJECT:
+    my_strcat("object");
+    /* Prog id */
+    break;
   case T_STRING: my_strcat("string"); break;
 
   case T_FUNCTION:
@@ -654,6 +679,13 @@ static char *low_match_types(char *a,char *b, int flags)
   /* 'mixed' matches anything */
   if(EXTRACT_UCHAR(a) == T_MIXED && !(flags & A_EXACT)) return a;
   if(EXTRACT_UCHAR(b) == T_MIXED && !(flags & B_EXACT)) return a;
+
+  /* Special case (tm) */
+  if(EXTRACT_UCHAR(a) == T_PROGRAM && EXTRACT_UCHAR(b)==T_FUNCTION)
+  {
+    return a;
+  }
+
   if(EXTRACT_UCHAR(a) != EXTRACT_UCHAR(b)) return 0;
 
   ret=a;
@@ -702,6 +734,13 @@ static char *low_match_types(char *a,char *b, int flags)
     if(!low_match_types(a+type_length(a),b+type_length(b),flags)) return 0;
     break;
 
+  case T_OBJECT:
+    a++;
+    b++;
+    if(!EXTRACT_INT(a) || !EXTRACT_INT(b)) break;
+    if(EXTRACT_INT(a) != EXTRACT_INT(b)) return 0;
+    break;
+
   case T_MULTISET:
   case T_ARRAY:
     if(!low_match_types(++a,++b,flags)) return 0;
@@ -709,7 +748,6 @@ static char *low_match_types(char *a,char *b, int flags)
   case T_INT:
   case T_FLOAT:
   case T_STRING:
-  case T_OBJECT:
   case T_PROGRAM:
   case T_VOID:
   case T_MIXED:
@@ -802,6 +840,11 @@ static int low_get_return_type(char *a,char *b)
       push_unfinished_type(a);
       return 1;
 
+    case T_PROGRAM:
+      push_type_int(0);
+      push_type(T_OBJECT);
+      return 1;
+
     default:
       push_type(T_MIXED);
       return 1;
diff --git a/src/program.c b/src/program.c
index b953546b4b..c46e051d6a 100644
--- a/src/program.c
+++ b/src/program.c
@@ -265,7 +265,7 @@ void check_program(struct program *p)
       fatal("Program ->prev == 0 but first_program != program.\n");
   }
 
-  if(p->id > current_program_id || p->id < 0)
+  if(p->id > current_program_id || p->id <= 0)
     fatal("Program id is wrong.\n");
 
   if(p->storage_needed < 0)
@@ -329,7 +329,7 @@ void check_program(struct program *p)
     check_string(p->identifiers[e].name);
     check_string(p->identifiers[e].type);
 
-    if(p->identifiers[e].flags & ~7)
+    if(p->identifiers[e].flags & ~15)
       fatal("Unknown flags in identifier flag field.\n");
 
     if(p->identifiers[e].run_time_type!=T_MIXED)
@@ -672,9 +672,13 @@ void do_inherit(struct program *p,INT32 flags, struct pike_string *name)
     inherit.storage_offset += storage_offset;
     inherit.inherit_level ++;
     add_to_mem_block(A_INHERITS,(char *)&inherit,sizeof inherit);
-
+    
     low_my_binary_strcat((char *)&name,sizeof(name),&inherit_names);
-    name=0;
+    if(name)
+    {
+      reference_shared_string(name);
+      name=0;
+    }
   }
 
   for (e=0; e < (int)p->num_identifier_references; e++)
@@ -742,6 +746,7 @@ void simple_do_inherit(struct pike_string *s, INT32 flags,struct pike_string *na
     s=name;
   }
   do_inherit(sp[-1].u.program, flags, s);
+  free_string(s);
   pop_stack();
 }
 
@@ -828,6 +833,57 @@ int define_variable(struct pike_string *name,
   return n;
 }
 
+int add_constant(struct pike_string *name,
+		 struct svalue *c,
+		 INT32 flags)
+{
+  int n;
+
+#ifdef DEBUG
+  if(name!=debug_findstring(name))
+    fatal("define_variable on nonshared string.\n");
+#endif
+
+  setup_fake_program();
+  n = isidentifier(name);
+
+  if(n != -1)
+  {
+    setup_fake_program();
+
+    if (IDENTIFIERP(n)->flags & ID_NOMASK)
+      my_yyerror("Illegal to redefine 'nomask' identifier \"%s\"", name->str);
+
+    if(PROG_FROM_INT(& fake_program, n) == &fake_program)
+      my_yyerror("Identifier '%s' defined twice.",name->str);
+  } else {
+    struct identifier dummy;
+    struct reference ref;
+
+    copy_shared_string(dummy.name, name);
+    dummy.type = get_type_of_svalue(c);
+    
+    dummy.flags = IDENTIFIER_CONSTANT;
+    dummy.run_time_type=c->type;
+    
+    dummy.func.offset=store_constant(c, 0);
+
+    ref.flags=flags;
+    ref.identifier_offset=areas[A_IDENTIFIERS].s.len / sizeof dummy;
+    ref.inherit_offset=0;
+
+    add_to_mem_block(A_IDENTIFIERS, (char *)&dummy, sizeof dummy);
+    fake_program.num_identifiers ++;
+
+    n=areas[A_IDENTIFIER_REFERENCES].s.len / sizeof ref;
+    add_to_mem_block(A_IDENTIFIER_REFERENCES, (char *)&ref, sizeof ref);
+    fake_program.num_identifier_references ++;
+
+  }
+
+  return n;
+}
+
 /*
  * define a new function
  * if func isn't given, it is supposed to be a prototype.
@@ -1222,8 +1278,6 @@ struct program *compile_file(struct pike_string *file_name)
   if(fd < 0)
     error("Couldn't open file '%s'.\n",file_name->str);
 
-
-
 #define FILE_STATE
 #define PUSH
 #include "compilation.h"
diff --git a/src/program.h b/src/program.h
index 04de1ccc1b..268cad7a29 100644
--- a/src/program.h
+++ b/src/program.h
@@ -74,6 +74,11 @@ union idptr
 #define IDENTIFIER_C_FUNCTION 2
 #define IDENTIFIER_FUNCTION 3
 #define IDENTIFIER_VARARGS 4
+#define IDENTIFIER_CONSTANT 8
+
+#define IDENTIFIER_IS_FUNCTION(X) ((X) & IDENTIFIER_FUNCTION)
+#define IDENTIFIER_IS_CONSTANT(X) ((X) & IDENTIFIER_CONSTANT)
+#define IDENTIFIER_IS_VARIABLE(X) (!((X) & (IDENTIFIER_FUNCTION | IDENTIFIER_CONSTANT)))
 
 struct identifier
 {
@@ -185,6 +190,9 @@ int isidentifier(struct pike_string *s);
 int define_variable(struct pike_string *name,
 		    struct pike_string *type,
 		    INT32 flags);
+int add_constant(struct pike_string *name,
+		 struct svalue *c,
+		 INT32 flags);
 INT32 define_function(struct pike_string *name,
 		      struct pike_string *type,
 		      INT16 flags,
diff --git a/src/testsuite.in b/src/testsuite.in
index a2e3410a33..004db5843a 100644
--- a/src/testsuite.in
+++ b/src/testsuite.in
@@ -1,3 +1,17 @@
+test_any([[object(File) o=File(); return objectp(o);]],1)
+test_any([[object o=Regexp("foo"); return objectp(o);]],1)
+test_any([[object o=Regexp("foo"); return object_program(o);]],Regexp)
+test_any([[class Test {}; object(Test) o=Test(); return object_program(o);]],Test)
+test_define_program(/test,[[constant foo = 1; int a() { return foo; }]])
+test_true(new("/test")->a())
+test_program(inherit "/test";)
+test_program(inherit "/test"; int a() { return foo; } )
+test_define_program(/test,[[class TEST { int a() { return 1; } }]])
+test_program(inherit "/test"; inherit TEST; )
+test_compile_error(class { object(File) foo; object(Regexp) bar=foo; })
+test_do(class { object foo; object(Regexp) bar=foo; })
+test_do(class { object(File) foo; object bar=foo; })
+
 // ++
 test_any([[int e; e++; return e;]],1)
 test_any([[int e; ++e; return e;]],1)
-- 
GitLab