pike.git / src / docode.c

version» Context lines:

pike.git/src/docode.c:1:   /*\   ||| This file a part of Pike, and is copyright by Fredrik Hubinette   ||| Pike is distributed as GPL (General Public License)   ||| See the files COPYING and DISCLAIMER for more information.   \*/   /**/   #include "global.h" - RCSID("$Id: docode.c,v 1.92 2001/01/11 14:12:30 grubba Exp $"); + RCSID("$Id: docode.c,v 1.93 2001/01/14 04:13:48 mast Exp $");   #include "las.h"   #include "program.h"   #include "pike_types.h"   #include "stralloc.h"   #include "interpret.h"   #include "constants.h"   #include "array.h"   #include "pike_macros.h"   #include "pike_error.h"   #include "pike_memory.h"
pike.git/src/docode.c:22:   #include "peep.h"   #include "docode.h"   #include "operators.h"   #include "object.h"   #include "opcodes.h"   #include "language.h"   #include "lex.h"      static int do_docode2(node *n, INT16 flags);    + typedef void (*cleanup_func)(void *); +  + struct cleanup_frame + { +  struct cleanup_frame *prev; +  cleanup_func cleanup; +  void *cleanup_arg; + }; +    struct statement_label_name   {    struct statement_label_name *next;    struct pike_string *str; -  unsigned INT16 line_number; +  unsigned int line_number;   };      struct statement_label   {    struct statement_label *prev;    struct statement_label_name *name; -  INT16 emit_break_label; +  /* -2 in break_label is used to flag "open" statement_label entries. +  * If an open entry is on top of the stack, it's used instead of a +  * new one. That's used to associate statement labels to the +  * following statement. */    INT32 break_label, continue_label; -  void (*cleanup)(void *); -  void *cleanup_arg; +  int emit_break_label; +  struct cleanup_frame *cleanups;   }; - static struct statement_label *current_label = 0; +     -  + static struct statement_label top_statement_label_dummy = +  {0, 0, -1, -1, 0, 0}; + static struct statement_label *current_label = &top_statement_label_dummy; +  + #define PUSH_CLEANUP_FRAME(func, arg) do { \ +  struct cleanup_frame cleanup_frame__; \ +  cleanup_frame__.cleanup = (cleanup_func) (func); \ +  cleanup_frame__.cleanup_arg = (void *)(ptrdiff_t) (arg); \ +  DO_IF_DEBUG( \ +  if (current_label->cleanups == (void *)(ptrdiff_t) -1) \ +  fatal("current_label points to an unused statement_label.\n"); \ +  ) \ +  if (current_label->break_label == -2) { \ +  DO_IF_DEBUG( \ +  if (current_label->prev->break_label == -2) \ +  fatal("Found two open statement_label entries in a row.\n"); \ +  ) \ +  cleanup_frame__.prev = current_label->prev->cleanups; \ +  current_label->prev->cleanups = &cleanup_frame__; \ +  } \ +  else { \ +  cleanup_frame__.prev = current_label->cleanups; \ +  current_label->cleanups = &cleanup_frame__; \ +  } +  + #define POP_AND_DONT_CLEANUP \ +  if (current_label->cleanups == &cleanup_frame__) \ +  current_label->cleanups = cleanup_frame__.prev; \ +  else { \ +  DO_IF_DEBUG( \ +  if (current_label->prev->cleanups != &cleanup_frame__) \ +  fatal("Cleanup frame lost from statement_label cleanup list.\n");\ +  ) \ +  current_label->prev->cleanups = cleanup_frame__.prev; \ +  } \ + } while (0) +  + #define POP_AND_DO_CLEANUP \ +  cleanup_frame__.cleanup(cleanup_frame__.cleanup_arg); \ +  POP_AND_DONT_CLEANUP +    #define PUSH_STATEMENT_LABEL do { \    struct statement_label new_label__; \    new_label__.prev = current_label; \ -  new_label__.cleanup = 0; \ -  if (!current_label || current_label->break_label != -2) { \ -  /* Only cover the current label if it's in use by a statement. */ \ +  if (current_label->break_label != -2) { \ +  /* Only cover the current label if it's closed. */ \    new_label__.name = 0; \    new_label__.break_label = new_label__.continue_label = -1; \ -  +  new_label__.cleanups = 0; \    current_label = &new_label__; \    } \ -  DO_IF_DEBUG( \ -  else if (current_label && current_label->cleanup) \ -  fatal("Cleanup callback taken in unused statement label.\n"); \ -  ) \ -  do +  DO_IF_DEBUG(else new_label__.cleanups = (void *)(ptrdiff_t) -1;)      #define POP_STATEMENT_LABEL \ -  while (0); \ +     current_label = new_label__.prev; \ -  if (new_label__.cleanup) \ -  new_label__.cleanup(new_label__.cleanup_arg); \ +  DO_IF_DEBUG( \ +  if (new_label__.cleanups && \ +  new_label__.cleanups != (void *)(ptrdiff_t) -1) \ +  fatal("Cleanup frames still left in statement_label.\n")); \   } while (0)      static INT32 current_switch_case;   static INT32 current_switch_default;   static INT32 current_switch_values_on_stack;   static INT32 *current_switch_jumptable =0;   static struct pike_string *current_switch_type = NULL;      void upd_int(int offset, INT32 tmp)   {
pike.git/src/docode.c:144:      int ins_label(int lbl)   {    if(lbl==-1) lbl=alloc_label();    low_insert_label(lbl);    return lbl;   }      void do_pop(int x)   { + #ifdef PIKE_DEBUG +  if (x < 0) fatal("Cannot do pop of %d args.\n", x); + #endif    switch(x)    {    case 0: return;    case 1: emit0(F_POP_VALUE); break;    default: emit1(F_POP_N_ELEMS,x); break;    }   }    -  + void do_cleanup_pop(int x) + { + #ifdef PIKE_DEBUG +  if(d_flag) +  emit0(F_POP_MARK); + #endif +  do_pop(x); + } +    void do_escape_catch()   {    emit0(F_ESCAPE_CATCH);   }      #define DO_CODE_BLOCK(X) do_pop(do_docode((X),DO_NOT_COPY | DO_POP | DO_DEFER_POP))      int do_docode(node *n, INT16 flags)   {    int i;
pike.git/src/docode.c:704:    emit0(F_DEC_AND_POP);    return 0;    }else{    emit0(n->token);    return 1;    }       case F_FOR:    {    INT32 *prev_switch_jumptable = current_switch_jumptable; -  PUSH_STATEMENT_LABEL { +  PUSH_STATEMENT_LABEL;       current_switch_jumptable=0;    current_label->break_label=alloc_label();    current_label->continue_label=alloc_label();       if(CDR(n))    {    do_jump_when_zero(CAR(n),current_label->break_label);    tmp2=ins_label(-1);    DO_CODE_BLOCK(CADR(n));    ins_label(current_label->continue_label);    DO_CODE_BLOCK(CDDR(n));    }else{    tmp2=ins_label(-1);    }    do_jump_when_non_zero(CAR(n), DO_NOT_WARN((INT32)tmp2));    ins_label(current_label->break_label);       current_switch_jumptable = prev_switch_jumptable; -  } POP_STATEMENT_LABEL; +  POP_STATEMENT_LABEL;    return 0;    }       case ' ':    return do_docode(CAR(n),0)+do_docode(CDR(n),DO_LVALUE);       case F_FOREACH:    {    node *arr;    INT32 *prev_switch_jumptable = current_switch_jumptable; -  PUSH_STATEMENT_LABEL { +  PUSH_CLEANUP_FRAME(do_cleanup_pop, 4); +  PUSH_STATEMENT_LABEL;       current_switch_jumptable=0;    current_label->break_label=alloc_label();    current_label->continue_label=alloc_label();       arr=CAR(n);       if(arr->token==F_RANGE)    {    node **a1=my_get_arg(&_CDR(n),0);
pike.git/src/docode.c:761:    {    tmp2=do_docode(CAR(arr),DO_NOT_COPY);    do_docode(*a1,DO_NOT_COPY);    goto foreach_arg_pushed;    }    }    tmp2=do_docode(CAR(n),DO_NOT_COPY);    emit0(F_CONST0);       foreach_arg_pushed: -  current_label->cleanup = (void (*)(void *)) do_pop; -  current_label->cleanup_arg = (void *) 4; +    #ifdef PIKE_DEBUG    /* This is really ugly because there is always a chance that the bug    * will disappear when new instructions are added to the code, but    * think it is worth it.    */    if(d_flag)    emit0(F_MARK);   #endif    tmp3=do_branch(-1);    tmp1=ins_label(-1);    DO_CODE_BLOCK(CDR(n));    ins_label(current_label->continue_label);    low_insert_label( DO_NOT_WARN((INT32)tmp3));    do_jump(n->token, DO_NOT_WARN((INT32)tmp1));    ins_label(current_label->break_label);    - #ifdef PIKE_DEBUG -  if(d_flag) -  emit0(F_POP_MARK); - #endif -  +     current_switch_jumptable = prev_switch_jumptable; -  } POP_STATEMENT_LABEL; +  POP_STATEMENT_LABEL; +  POP_AND_DO_CLEANUP;    return 0;    }       case F_INC_NEQ_LOOP:    case F_DEC_NEQ_LOOP:    case F_INC_LOOP:    case F_DEC_LOOP:    {    INT32 *prev_switch_jumptable = current_switch_jumptable; -  PUSH_STATEMENT_LABEL { +  PUSH_CLEANUP_FRAME(do_cleanup_pop, 3); +  PUSH_STATEMENT_LABEL;       current_switch_jumptable=0;    current_label->break_label=alloc_label();    current_label->continue_label=alloc_label();       tmp2=do_docode(CAR(n),0); -  current_label->cleanup = (void (*)(void *)) do_pop; -  current_label->cleanup_arg = (void *) 3; +    #ifdef PIKE_DEBUG    /* This is really ugly because there is always a chance that the bug    * will disappear when new instructions are added to the code, but    * think it is worth it.    */    if(d_flag)    emit0(F_MARK);   #endif    tmp3=do_branch(-1);    tmp1=ins_label(-1);       DO_CODE_BLOCK(CDR(n));    ins_label(current_label->continue_label);    low_insert_label( DO_NOT_WARN((INT32)tmp3));    do_jump(n->token, DO_NOT_WARN((INT32)tmp1));    ins_label(current_label->break_label); - #ifdef PIKE_DEBUG -  if(d_flag) -  emit0(F_POP_MARK); - #endif +        current_switch_jumptable = prev_switch_jumptable; -  } POP_STATEMENT_LABEL; +  POP_STATEMENT_LABEL; +  POP_AND_DO_CLEANUP;    return 0;    }       case F_DO:    {    INT32 *prev_switch_jumptable = current_switch_jumptable; -  PUSH_STATEMENT_LABEL { +  PUSH_STATEMENT_LABEL;       current_switch_jumptable=0;    current_label->break_label=alloc_label();    current_label->continue_label=alloc_label();       tmp2=ins_label(-1);    DO_CODE_BLOCK(CAR(n));    ins_label(current_label->continue_label);    do_jump_when_non_zero(CDR(n), DO_NOT_WARN((INT32)tmp2));    ins_label(current_label->break_label);       current_switch_jumptable = prev_switch_jumptable; -  } POP_STATEMENT_LABEL; +  POP_STATEMENT_LABEL;    return 0;    }       case F_POP_VALUE:    {    DO_CODE_BLOCK(CAR(n));    return 0;    }       case F_CAST:
pike.git/src/docode.c:999:    INT32 e,cases,*order;    INT32 *jumptable;    INT32 prev_switch_values_on_stack = current_switch_values_on_stack;    INT32 prev_switch_case = current_switch_case;    INT32 prev_switch_default = current_switch_default;    INT32 *prev_switch_jumptable = current_switch_jumptable;    struct pike_string *prev_switch_type = current_switch_type;   #ifdef PIKE_DEBUG    struct svalue *save_sp=Pike_sp;   #endif -  PUSH_STATEMENT_LABEL { +  PUSH_STATEMENT_LABEL;       if(do_docode(CAR(n),0)!=1)    fatal("Internal compiler error, time to panic\n");       if (!(CAR(n) && (current_switch_type = CAR(n)->type))) {    current_switch_type = mixed_type_string;    }       current_label->break_label=alloc_label();   
pike.git/src/docode.c:1092:    free((char *)current_switch_jumptable);       current_switch_jumptable = prev_switch_jumptable;    current_switch_default = prev_switch_default;    current_switch_case = prev_switch_case;    current_switch_values_on_stack = prev_switch_values_on_stack;    current_switch_type = prev_switch_type;       low_insert_label( current_label->break_label);    -  } POP_STATEMENT_LABEL; +  POP_STATEMENT_LABEL;   #ifdef PIKE_DEBUG    if(Pike_interpreter.recoveries && Pike_sp-Pike_interpreter.evaluator_stack < Pike_interpreter.recoveries->stack_pointer)    fatal("Stack error after F_SWITCH (underflow)\n");   #endif    return 0;    }       case F_CASE:    {    if(!current_switch_jumptable)
pike.git/src/docode.c:1220:    if (n->token == F_CONTINUE && label->continue_label < 0) {    my_yyerror("Cannot continue the non-loop statement on line %d.",    lbl_name->line_number);    return 0;    }    }       else    if (n->token == F_BREAK) {    label = current_label; -  if(!label || label->break_label < 0) +  if(label->break_label < 0)    {    yyerror("Break outside loop or switch.");    return 0;    }    }    else {    for (label = current_label; label; label = label->prev)    if (label->continue_label >= 0)    goto continue_label_found;    yyerror("Continue outside loop.");    return 0;    continue_label_found:    ;    }    -  for (p = current_label; p != label; p = p->prev) -  if (p->cleanup) -  p->cleanup(p->cleanup_arg); +  for (p = current_label; 1; p = p->prev) { +  struct cleanup_frame *q; +  for (q = p->cleanups; q; q = q->prev) +  q->cleanup(q->cleanup_arg); +  if (p == label) break; +  }       if (n->token == F_BREAK) {    if (label->break_label < 0) label->emit_break_label = 1;    label->break_label = do_branch(label->break_label);    }    else    do_branch(label->continue_label);       return 0;    }       case F_NORMAL_STMT_LABEL: -  case F_CUSTOM_STMT_LABEL: -  PUSH_STATEMENT_LABEL { +  case F_CUSTOM_STMT_LABEL: {    struct statement_label *label;    struct statement_label_name name; -  +  PUSH_STATEMENT_LABEL;    name.str = CAR(n)->u.sval.u.string;    name.line_number = n->line_number;       for (label = current_label; label; label = label->prev) {    struct statement_label_name *lbl_name;    for (lbl_name = label->name; lbl_name; lbl_name = lbl_name->next)    if (lbl_name->str == name.str) {    INT32 save_line = lex.current_line;    lex.current_line = name.line_number;    my_yyerror("Duplicate nested labels, previous one on line %d.",    lbl_name->line_number);    lex.current_line = save_line; -  break; +  goto label_check_done;    }    } -  +  label_check_done:       name.next = current_label->name;    current_label->name = &name;       if (!name.next) {    current_label->emit_break_label = 0;    if (n->token == F_CUSTOM_STMT_LABEL)    /* The statement we precede has custom label handling; leave    * the statement_label "open" so the statement will use it    * instead of covering it. */    current_label->break_label = -2;    else    current_label->break_label = -1;    }    DO_CODE_BLOCK(CDR(n));    if (!name.next && current_label->emit_break_label)    low_insert_label(current_label->break_label); -  } POP_STATEMENT_LABEL; +  POP_STATEMENT_LABEL;    return 0; -  +  }       case F_RETURN:    do_docode(CAR(n),0);    emit0(F_RETURN);    return 0;       case F_SSCANF:    tmp1=do_docode(CAR(n),DO_NOT_COPY);    tmp2=do_docode(CDR(n),DO_NOT_COPY | DO_LVALUE);    emit1(F_SSCANF, DO_NOT_WARN((INT32)(tmp1+tmp2)));    return 1;    -  case F_CATCH: -  PUSH_STATEMENT_LABEL { +  case F_CATCH: {    INT32 *prev_switch_jumptable = current_switch_jumptable; -  +  PUSH_STATEMENT_LABEL;       current_switch_jumptable=0;    current_label->break_label=alloc_label();    if (TEST_COMPAT(7,0))    current_label->continue_label=alloc_label(); -  current_label->cleanup = (void (*)(void *)) do_escape_catch; +  PUSH_CLEANUP_FRAME(do_escape_catch, 0);       tmp1=do_jump(F_CATCH,-1);    DO_CODE_BLOCK(CAR(n));       if (TEST_COMPAT(7,0))    ins_label(current_label->continue_label);    ins_label(current_label->break_label);    emit0(F_THROW_ZERO);    ins_label(DO_NOT_WARN((INT32)tmp1));       current_switch_jumptable = prev_switch_jumptable; -  current_label->cleanup = 0; -  } POP_STATEMENT_LABEL; +  POP_AND_DONT_CLEANUP; +  POP_STATEMENT_LABEL;    return 1; -  +  }       case F_LVALUE_LIST:    return do_docode(CAR(n),DO_LVALUE)+do_docode(CDR(n),DO_LVALUE);       case F_ARRAY_LVALUE:    tmp1=do_docode(CAR(n),DO_LVALUE);   #ifdef PIKE_DEBUG    if(tmp1 & 1)    fatal("Very internal compiler error.\n");   #endif