Diff for /parser3/src/classes/op.C between versions 1.127.2.22.2.8 and 1.153.2.2

version 1.127.2.22.2.8, 2003/03/21 14:27:50 version 1.153.2.2, 2005/07/27 10:57:52
Line 1 Line 1
 /** @file  /** @file
         Parser: parser @b operators.          Parser: parser @b operators.
   
         Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
 */  */
   
 static const char* IDENT_OP_C="$Date$";  static const char * const IDENT_OP_C="$Date$";
   
 #include "classes.h"  #include "classes.h"
 #include "pa_vmethod_frame.h"  #include "pa_vmethod_frame.h"
   
 #include "pa_common.h"  #include "pa_common.h"
   #include "pa_os.h"
 #include "pa_request.h"  #include "pa_request.h"
 #include "pa_vint.h"  #include "pa_vint.h"
 #include "pa_sql_connection.h"  #include "pa_sql_connection.h"
 #include "pa_vdate.h"  #include "pa_vdate.h"
 #include "pa_vmethod_frame.h"  #include "pa_vmethod_frame.h"
 #include "pa_vclass.h"  #include "pa_vclass.h"
   #include "pa_charset.h"
   
 // limits  // limits
   
Line 25  static const char* IDENT_OP_C="$Date$"; Line 27  static const char* IDENT_OP_C="$Date$";
 // defines  // defines
   
 #define CASE_DEFAULT_VALUE "DEFAULT"  #define CASE_DEFAULT_VALUE "DEFAULT"
   #define PROCESS_MAIN_OPTION_NAME "main"
   #define PROCESS_FILE_OPTION_NAME "file"
   #define PROCESS_LINENO_OPTION_NAME "lineno"
   
 // class  // class
   
 class VClassMAIN: public VClass {  class VClassMAIN: public VClass {
 public:  public:
         VClassMAIN();          VClassMAIN();
         override ~VClassMAIN() {}  
 };  };
   
 // defines for statics  // defines for statics
Line 39  public: Line 43  public:
 #define SWITCH_DATA_NAME "SWITCH-DATA"  #define SWITCH_DATA_NAME "SWITCH-DATA"
 #define CACHE_DATA_NAME "CACHE-DATA"  #define CACHE_DATA_NAME "CACHE-DATA"
 #define EXCEPTION_VAR_NAME "exception"  #define EXCEPTION_VAR_NAME "exception"
 #define EXCEPTION_TYPE_PART_NAME "type"  
 #define EXCEPTION_SOURCE_PART_NAME "source"  
 #define EXCEPTION_COMMENT_PART_NAME "comment"  
 #define EXCEPTION_HANDLED_PART_NAME "handled"  
   
 // statics  // statics
   
 //^switch ^case  //^switch ^case
 static const String& switch_data_name(new String(SWITCH_DATA_NAME));  static const String switch_data_name(SWITCH_DATA_NAME);
 //^cache  //^cache
 static const String& cache_data_name(new String(CACHE_DATA_NAME));  static const String cache_data_name(CACHE_DATA_NAME);
   
 static const String& exception_var_name(new String(EXCEPTION_VAR_NAME));  static const String exception_var_name(EXCEPTION_VAR_NAME);
 static const String& exception_type_part_name(new String(EXCEPTION_TYPE_PART_NAME));  
 static const String& exception_source_part_name(new String(EXCEPTION_SOURCE_PART_NAME));  // local defines
 static const String& exception_comment_part_name(new String(EXCEPTION_COMMENT_PART_NAME));  
 static const String& exception_handled_part_name(new String(EXCEPTION_HANDLED_PART_NAME));  #define CACHE_EXCEPTION_HANDLED_CACHE_NAME "cache"
   
 // helpers  // helpers
   
 class Untaint_lang_name2enum: public Hash<String* , int> {  class Untaint_lang_name2enum: public Hash<const String::Body, String::Language> {
 public:  public:
         Untaint_lang_name2enum() {          Untaint_lang_name2enum() {
                 #define ULN(name, LANG) \                  #define ULN(name, LANG) \
                         put(String* (new String(name)), (value_type)String::L_##LANG);                          put(String::Body(name), (value_type)(String::L_##LANG));
                 ULN("as-is", AS_IS);                  ULN("as-is", AS_IS);
                 ULN("optimized-as-is", AS_IS|String::L_OPTIMIZE_BIT);                  ULN("optimized-as-is", AS_IS|String::L_OPTIMIZE_BIT);
                 ULN("file-spec", FILE_SPEC);                  ULN("file-spec", FILE_SPEC);
                 ULN("http-header", HTTP_HEADER);                  ULN("http-header", HTTP_HEADER);
                 ULN("mail-header", MAIL_HEADER);                  ULN("mail-header", MAIL_HEADER);
                 ULN("uri", URI);                  ULN("uri", URI);
                 ULN("table", TABLE);  
                 ULN("sql", SQL);                  ULN("sql", SQL);
                 ULN("js", JS);                  ULN("js", JS);
                 ULN("xml", XML);                  ULN("xml", XML);
Line 83  public: Line 82  public:
   
 // methods  // methods
   
 static void _if(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _if(Request& r, MethodParams& params) {
         Value* condition_code=params->as_junction(0, "condition must be expression");          Value& condition_code=params.as_junction(0, "condition must be expression");
   
         bool condition=r.process_to_value(condition_code,           bool condition=r.process_to_value(condition_code, 
                 /*0/*no name* /,*/                  false/*don't intercept string*/).as_bool();
                 false/*don't intercept string*/)->as_bool();  
         if(condition)          if(condition)
                 r.write_pass_lang(r.process(params->as_junction(1, "'then' parameter must be code")));                  r.write_pass_lang(r.process(params.as_junction(1, "'then' parameter must be code")));
         else if(params->count()>2)          else if(params.count()>2)
                 r.write_pass_lang(r.process(params->as_junction(2, "'else' parameter must be code")));                  r.write_pass_lang(r.process(params.as_junction(2, "'else' parameter must be code")));
 }  }
   
 static void _untaint(Request& r, const String& method_name, MethodParams* params) {  static void _untaint(Request& r, MethodParams& params) {
   
         Language lang;          String::Language lang;
         if(params->count()==1)          if(params.count()==1)
                 lang=String::L_AS_IS; // mark as simply 'tainted'. useful in html from sql                   lang=String::L_AS_IS; // mark as simply 'tainted'. useful in html from sql 
         else {          else {
                 const String& lang_name=params->as_string(0, "lang must be string");                  const String& lang_name=params.as_string(0, "lang must be string");
                 lang=untaint_lang_name2enum.get(lang_name);                  lang=untaint_lang_name2enum.get(lang_name);
                 if(!lang)                  if(!lang)
                         throw Exception(0,                          throw Exception(0,
                                 lang_name,                                  &lang_name,
                                 "invalid taint language");                                  "invalid taint language");
         }          }
   
         {          {
                 Value* vbody=params->as_junction(params->count()-1, "body must be code");                  Value& vbody=params.as_junction(params.count()-1, "body must be code");
                                   
                 Temp_lang temp_lang(r, lang); // set temporarily specified ^untaint[language;                  Temp_lang temp_lang(r, lang); // set temporarily specified ^untaint[language;
                 r.write_pass_lang(r.process(vbody)); // process marking tainted with that lang                  r.write_pass_lang(r.process(vbody)); // process marking tainted with that lang
         }          }
 }  }
   
 static void _taint(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _taint(Request& r, MethodParams& params) {
           String::Language lang;
         Language lang;          if(params.count()==1)
         if(params->count()==1)  
                 lang=String::L_TAINTED; // mark as simply 'tainted'. useful in table:set                  lang=String::L_TAINTED; // mark as simply 'tainted'. useful in table:set
         else {          else {
                 const String& lang_name=params->as_string(0, "lang must be string");                  const String& lang_name=params.as_string(0, "lang must be string");
                 lang=untaint_lang_name2enum.get(lang_name);                  lang=untaint_lang_name2enum.get(lang_name);
                 if(!lang)                  if(!lang)
                         throw Exception(0,                          throw Exception(0,
                                 lang_name,                                  &lang_name,
                                 "invalid taint language");                                  "invalid taint language");
         }          }
   
         {          {
                 Value* vbody=params->as_no_junction(params->count()-1, "body must not be code");                  Value& vbody=params.as_no_junction(params.count()-1, "body must not be code");
                                   
                 String result;                  String result;
                 result.append(                  result.append(
                         *vbody->as_string(),  // process marking tainted with that lang                          vbody.as_string(),  // process marking tainted with that lang
                         lang, true);  // force result language to specified                          lang, true);  // force result language to specified
                 r.write_pass_lang(result);                  r.write_pass_lang(result);
         }          }
 }  }
   
 static void _process(Request& r, const String& method_name, MethodParams* params) {  static void _process(Request& r, MethodParams& params) {
         const String* source; // must be declared here to prevent premature destruction  
         Method* main_method;          Method* main_method;
         Value* target_self=params->count()>1?  
                 params->as_no_junction(0, "target must not be code")  
                 :Value*(&r.get_method_frame()->caller()->self());  
         {  
                 Value* vjunction=params->as_junction(params->count()-1, "body must be code");  
   
           size_t index=0;
           Value* target_self;
           Value& maybe_target_self=params[index];
           if(maybe_target_self.get_string() || maybe_target_self.get_junction())
                   target_self=&r.get_method_frame()->caller()->self();
           else {
                   target_self=&maybe_target_self;  index++;
           }
   
           {
                 VStateless_class *target_class=target_self->get_last_derived_class();                  VStateless_class *target_class=target_self->get_last_derived_class();
                 if(!target_class)                  if(!target_class)
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 method_name,                                  0,
                                 "no target class");                                  "no target class");
                   
                 // evaluate source to process  
                 source=r.process_to_string(vjunction);  
   
                 // temporary remove language change                  // temporary remove language change
                 Temp_lang temp_lang(r, String::L_PASS_APPENDED);                  Temp_lang temp_lang(r, String::L_PASS_APPENDED);
                 // temporary zero @main so to maybe-replace it in processed code                  // temporary zero @main so to maybe-replace it in processed code
                 Temp_method temp_method_main(*target_class, main_method_name, Method*(0));                  Temp_method temp_method_main(*target_class, main_method_name, 0);
                 // temporary zero @auto so it wouldn't be auto-called in Request::use_buf                  // temporary zero @auto so it wouldn't be auto-called in Request::use_buf
                 Temp_method temp_method_auto(*target_class, auto_method_name, Method*(0));                  Temp_method temp_method_auto(*target_class, auto_method_name, 0);
                   
                 // calculate pseudo file name of processed chars  
                 // would be something like "/some/file(4) process"  
                 char local_place[MAX_STRING];  
 #ifndef NO_STRING_ORIGIN  
                 const String_fragment::Origin& source_origin=source->origin();  
                 const String_fragment::Origin& method_origin=method_name->origin();  
                 size_t place_size;  
                 if(source_origin.file==method_origin.file)  
                         place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s",  // same file  
                                 source_origin.file?source_origin.file:"unknown_file", 1+source_origin.line,   
                                 method_name->cstr().get())+1;  
                 else // different files ^process{external__file_text__or__sql}  
                         place_size=snprintf(local_place, MAX_STRING, "%s",   
                                 source_origin.file?source_origin.file:"unknown_file")+1;  
 #else  
                 strncpy(local_place, method_name->cstr(), MAX_STRING-1); place[MAX_STRING-1]=0;  
                 size_t place_size=strlen(local_place)+1;  
 #endif  
                 char *heap_place=pool.copy(local_place, place_size);  
   
                 // process source code, append processed methods to 'self' class                  size_t options_index=index+1;
                 // maybe-define new @main                  HashStringValue* options=0;
                   if(options_index<params.count()) {
                           Value& voptions=params.as_no_junction(options_index, "options must not be code");
                           options=voptions.get_hash();
                           if(!options)
                                   throw Exception("parser.runtime",
                                           0,
                                           "options must be hash");
                   }
   
                   const String* main_alias=0;
                   const String* file_alias=0;
                   int line_no_alias_offset=0;
                   if(options) {
                           int valid_options=0;
                           if(Value* vmain_alias=options->get(PROCESS_MAIN_OPTION_NAME)) {
                                   valid_options++;
                                   main_alias=&vmain_alias->as_string();
                           }
                           if(Value* vfile_alias=options->get(PROCESS_FILE_OPTION_NAME)) {
                                   valid_options++;
                                   file_alias=&vfile_alias->as_string();
                           }
                           if(Value* vline_no_alias_offset=options->get(PROCESS_LINENO_OPTION_NAME)) {
                                   valid_options++;
                                   line_no_alias_offset=vline_no_alias_offset->as_int();
                           }
           
                           if(valid_options!=options->count())
                                   throw Exception("parser.runtime",
                                           0,
                                           "called with invalid option");
                   }
   
                   uint processe_file_no=file_alias?
                           r.register_file(r.absolute(*file_alias))
                           : pseudo_file_no__process;
                   // process...{string}
                   Value& vjunction=params.as_junction(index, "body must be code");
                   // evaluate source to process
                   const String& source=r.process_to_string(vjunction);
                 r.use_buf(*target_class,                  r.use_buf(*target_class,
                         source->cstr(String::L_UNSPECIFIED, r.connection(0)),                           source.cstr(String::L_UNSPECIFIED, r.connection(false)),
                         String* (new String(heap_place, place_size, true /*tainted*/)),                          main_alias,
                         heap_place);                          processe_file_no,
                                           line_no_alias_offset);
   
                 // main_method                  // main_method
                 main_method=target_class->get_method(main_method_name);                  main_method=target_class->get_method(main_method_name);
         }          }
Line 201  static void _process(Request& r, const S Line 220  static void _process(Request& r, const S
         // maybe-execute @main[]          // maybe-execute @main[]
         if(main_method) {          if(main_method) {
                 // temporarily set method_frame's self to target_self                  // temporarily set method_frame's self to target_self
                 Temp_method_frame_self tmfs(*r.get_method_frame(), target_self);                  Temp_method_frame_self tmfs(*r.get_method_frame(), *target_self);
                 // execute!                       // execute!     
                 r.execute(*main_method->parser_code);                  r.execute(*main_method->parser_code);
         }          }
 }  }
                   
 static void _rem(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _rem(Request&, MethodParams& params) {
         params->as_junction(0, "body must be code");          params.as_junction(0, "body must be code");
 }  }
   
 static void _while(Request& r, const String& method_name, MethodParams* params) {  static void _while(Request& r, MethodParams& params) {
           Value& vcondition=params.as_junction(0, "condition must be expression");
         Value* vcondition=params->as_junction(0, "condition must be expression");          Value& body=params.as_junction(1, "body must be code");
         Value* body=params->as_junction(1, "body must be code");  
   
         // while...          // while...
         int endless_loop_count=0;          int endless_loop_count=0;
         while(true) {          while(true) {
                 if(++endless_loop_count>=MAX_LOOPS) // endless loop?                  if(++endless_loop_count>=MAX_LOOPS) // endless loop?
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 method_name,                                  0,
                                 "endless loop detected");                                  "endless loop detected");
   
                 bool condition=r.process_to_value(vcondition,                   bool condition=r.process_to_value(vcondition, 
                                 /*0/*no name* /,*/                                  false/*don't intercept string*/).as_bool();
                                 false/*don't intercept string*/)->as_bool();  
                 if(!condition) // ...condition is true                  if(!condition) // ...condition is true
                         break;                          break;
   
Line 235  static void _while(Request& r, const Str Line 252  static void _while(Request& r, const Str
         }          }
 }  }
   
 static void _use(Request& r, const String& method_name, MethodParams* params) {  static void _use(Request& r, MethodParams& params) {
         Value* vfile=params->as_no_junction(0, "file name must not be code");          Value& vfile=params.as_no_junction(0, "file name must not be code");
         r.use_file(*r.main_class, vfile->as_string());          r.use_file(r.main_class, vfile.as_string());
 }  }
   
 static void _for(Request& r, const String& method_name, MethodParams* params) {  static void _for(Request& r, MethodParams& params) {
         const String& var_name=params->as_string(0, "var name must be string");          const String& var_name=params.as_string(0, "var name must be string");
         int from=params->as_int(1, "from must be int", r);          int from=params.as_int(1, "from must be int", r);
         int to=params->as_int(2, "to must be int", r);          int to=params.as_int(2, "to must be int", r);
         Value*  body_code=params->as_junction(3, "body must be code");          Value&  body_code=params.as_junction(3, "body must be code");
         Value* delim_maybe_code=params->count()>4?(*params)[4]:0;          Value* delim_maybe_code=params.count()>4?&params[4]:0;
   
         if(to-from>=MAX_LOOPS) // too long loop?          if(to-from>=MAX_LOOPS) // too long loop?
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         method_name,                          0,
                         "endless loop detected");                          "endless loop detected");
   
         bool need_delim=false;          bool need_delim=false;
         VIntPtr vint(new VInt(0));          VInt* vint=new VInt(0);
         r.get_method_frame()->caller()->put_element(var_name, vint, false);  
           VMethodFrame& caller=*r.get_method_frame()->caller();
           caller.put_element(caller, var_name, vint);
         for(int i=from; i<=to; i++) {          for(int i=from; i<=to; i++) {
                 vint->set_int(i);                  vint->set_int(i);
   
                 StringOrValue sv_processed=r.process(body_code);                  StringOrValue sv_processed=r.process(body_code);
                 const String& s_processed=sv_processed.get_string();                  const String* s_processed=sv_processed.get_string();
                 if(delim_maybe_code && s_processed && s_processed->length()) { // delimiter set and we have body                  if(delim_maybe_code && s_processed && s_processed->length()) { // delimiter set and we have body
                         if(need_delim) // need delim & iteration produced string?                          if(need_delim) // need delim & iteration produced string?
                                 r.write_pass_lang(r.process(delim_maybe_code));                                  r.write_pass_lang(r.process(*delim_maybe_code));
                         need_delim=true;                          need_delim=true;
                 }                  }
                 r.write_pass_lang(sv_processed);                  r.write_pass_lang(sv_processed);
         }          }
 }  }
   
 static void _eval(Request& r, const String& method_name, MethodParams* params) {  static void _eval(Request& r, MethodParams& params) {
         Value* expr=params->as_junction(0, "need expression");          Value& expr=params.as_junction(0, "need expression");
         // evaluate expresion          // evaluate expresion
         Value* value_result=r.process_to_value(expr,           Value& value_result=r.process_to_value(expr, 
                 /*0/*no name YET* /,*/                  true/*don't intercept string*/).as_expr_result();
                 true/*don't intercept string*/)->as_expr_result();          if(params.count()>1) {
         if(params->count()>1) {                  Value& fmt=params.as_no_junction(1, "fmt must not be code");
                 Value* fmt=params->as_no_junction(1, "fmt must not be code");                  r.write_no_lang(String(format(value_result.as_double(), fmt.as_string().cstrm())));
                 String string;  
                 string.APPEND_CONST(  
                         format(value_result->as_double(), fmt->as_string().cstr()));  
                 r.write_no_lang(string);  
         } else          } else
                 r.write_no_lang(value_result);                  r.write_no_lang(value_result);
 }  }
   
 static void _connect(Request& r, const String& method_name, MethodParams* params) {  static void _connect(Request& r, MethodParams& params) {
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 struct timeval mt[2];  struct timeval mt[2];
 #endif  #endif
         Value* url=params->as_no_junction(0, "url must not be code");          Value& url=params.as_no_junction(0, "url must not be code");
         Value* body_code=params->as_junction(1, "body must be code");          Value& body_code=params.as_junction(1, "body must be code");
   
         Table* protocol2driver_and_client=0;          Table* protocol2driver_and_client=0;
         if(Value* sql=r.main_class->get_element(String* (new String(MAIN_SQL_NAME)), *r.main_class, false)) {          if(Value* sql=r.main_class.get_element(String(MAIN_SQL_NAME), r.main_class, false)) {
                 if(Value* element=sql->get_element(String* (new String(MAIN_SQL_DRIVERS_NAME)), *sql, false)) {                  if(Value* element=sql->get_element(String(MAIN_SQL_DRIVERS_NAME), *sql, false)) {
                         protocol2driver_and_client=element->get_table();                          protocol2driver_and_client=element->get_table();
                 }                  }
         }          }
Line 304  struct timeval mt[2]; Line 319  struct timeval mt[2];
 gettimeofday(&mt[0],NULL);  gettimeofday(&mt[0],NULL);
 #endif  #endif
         // connect          // connect
         SQL_ConnectionPtr connection=SQL_driver_manager.get_connection(pool,          SQL_Connection* connection=SQL_driver_manager->get_connection(url.as_string(), 
                 url->as_string(), method_name, protocol2driver_and_client);                  protocol2driver_and_client,
                   r.charsets.source().NAME().cstr());
   
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 //measure:after connect  //measure:after connect
Line 317  for(int i=0;i<2;i++) Line 333  for(int i=0;i<2;i++)
   
 r.sql_connect_time+=t[1]-t[0];  r.sql_connect_time+=t[1]-t[0];
 #endif  #endif
         Temp_connection temp_connection(r, connection.get());          Temp_connection temp_connection(r, connection);
         // execute body          // execute body
         try {          try {
                 r.write_assign_lang(r.process(body_code));                  r.write_assign_lang(r.process(body_code));
                   connection->commit();
                   connection->close();
         } catch(...) { // process problem          } catch(...) { // process problem
                 connection->mark_to_rollback();                  connection->rollback();
                 rethrow;                   connection->close();
                   rethrow;
         }          }
 }  }
   
Line 331  r.sql_connect_time+=t[1]-t[0]; Line 350  r.sql_connect_time+=t[1]-t[0];
 class Switch_data: public PA_Object {  class Switch_data: public PA_Object {
 public:  public:
         Request& r;          Request& r;
         Value* searching;          Value& searching;
         Value* found;          Value* found;
         Value* _default;          Value* _default;
 public:  public:
         Switch_data(Request& ar, Value* asearching):           Switch_data(Request& ar, Value& asearching): 
           r(ar), searching(asearching) {}            r(ar), searching(asearching) {}
 };  };
 DECLARE_OBJECT_PTR(Switch_data);  
 #endif  #endif
 static void _switch(Request& r, const String& /*method_name*/, MethodParams* params) {  static void _switch(Request& r, MethodParams& params) {
         Switch_dataPtr data(new Switch_data(r, r.process_to_value(params->get(0))));          Switch_data* data=new Switch_data(r, r.process_to_value(params[0]));
         Temp_hash_value<String* , PA_Object*>           Temp_hash_value<const String::Body, void*> 
                 switch_data_setter(r.classes_conf, switch_data_name, data);                  switch_data_setter(r.classes_conf, switch_data_name, data);
   
         Value* cases_code=params->as_junction(1, "switch cases must be code");          Value& cases_code=params.as_junction(1, "switch cases must be code");
         // execution of found ^case[...]{code} must be in context of ^switch[...]{code}          // execution of found ^case[...]{code} must be in context of ^switch[...]{code}
         // because of stacked WWrapper used there as wcontext          // because of stacked WWrapper used there as wcontext
         r.process(cases_code, true/*intercept_string*/);          r.process(cases_code, true/*intercept_string*/);
         if(Value* selected_code=data->found? data->found: data->_default) {          if(Value* selected_code=data->found? data->found: data->_default)
                 // setting code context, would execute in ^switch[...]{>>context<<}                  r.write_pass_lang(r.process(*selected_code));
                 //selected_code->get_junction()->change_context(cases_code.get_junction());  
                 r.write_pass_lang(r.process(selected_code));  
         }  
 }  }
   
 static void _case(Request& r, const String& method_name, MethodParams* params) {  static void _case(Request& r, MethodParams& params) {
           Switch_data* data=static_cast<Switch_data*>(r.classes_conf.get(switch_data_name));
         Switch_data* data=static_cast<Switch_data*>(r.classes_conf.get(switch_data_name).get());  
         if(!data)          if(!data)
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         method_name,                          0,
                         "without switch");                          "without switch");
   
         int count=params->count();          int count=params.count();
         Value* code=params->as_junction(--count, "case result must be code");          Value& code=params.as_junction(--count, "case result must be code");
                   
         // killing context for safety, would execute in ^switch[...]{>>context<<}  
         // reason: context is stacked, and it would become invalid afterwards  
         //code->get_junction()->change_context(0);  
   
         for(int i=0; i<count; i++) {          for(int i=0; i<count; i++) {
                 Value* value=r.process_to_value(params->get(i));                  Value& value=r.process_to_value(params[i]);
   
                 if(*value->as_string() == CASE_DEFAULT_VALUE) {                  if(value.as_string() == CASE_DEFAULT_VALUE) {
                         data->_default=code;                          data->_default=&code;
                         break;                          break;
                 }                  }
   
                 bool matches;                  bool matches;
                 if(data->searching->is_string())                  if(data->searching.is_string())
                         matches=*data->searching->as_string() == *value->as_string();                          matches=data->searching.as_string() == value.as_string();
                 else                  else
                         matches=data->searching->as_double() == value->as_double();                          matches=data->searching.as_double() == value.as_double();
   
                 if(matches) {                  if(matches) {
                         if(data->found)                          if(data->found)
                                 throw Exception("parser.runtime",                                  throw Exception("parser.runtime",
                                         method_name,                                          0,
                                         "duplicate found");                                          "duplicate found");
   
                         data->found=code;                          data->found=&code;
                         break;                          break;
                 }                  }
         }          }
 }  }
   #ifndef DOXYGEN
   struct Try_catch_result {
           StringOrValue processed_code;
           const String* exception_should_be_handled;
   
           Try_catch_result(): exception_should_be_handled(0) {}
   };
   #endif
   
   /// used by ^try and ^cache, @returns $exception.handled[string] if any
   template<class I>
   static Try_catch_result try_catch(Request& r, 
                     StringOrValue body_code(Request&, I), I info, 
                     Value* catch_code) 
   {
           Try_catch_result result;
           if(!catch_code) {
                   result.processed_code=body_code(r, info);
                   return result;
           }
   
           // taking snapshot of try-context
           Request_context_saver try_context(r);
           try {
                   result.processed_code=body_code(r, info);
           } catch(const Exception& e) {
                   Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error]
                   Request::Exception_details details=r.get_details(e);
                   try_context.restore(); // restoring try-context to perform catch-code
   
                   Junction* junction=catch_code->get_junction();
                   Value* method_frame=junction->method_frame;
                   Value* saved_exception_var_value=method_frame->get_element(exception_var_name, *method_frame, false);
                   VMethodFrame& frame=*junction->method_frame;
                   frame.put_element(frame, exception_var_name, &details.vhash);
                   result.processed_code=r.process(*catch_code);
                   
                   // retriving $exception.handled, restoring $exception var
                   Value* vhandled=details.vhash.hash().get(exception_handled_part_name);
                   frame.put_element(frame, exception_var_name, saved_exception_var_value);
   
                   bool bhandled=false;
                   if(vhandled) {
                           if(vhandled->is_string()) { // not simple $exception.handled(1/0)?
                                   result.exception_should_be_handled=vhandled->get_string(); // considering 'recovered' and let the caller recover
                                   return result;
                           }
   
                           bhandled=vhandled->as_bool();           
                   }
   
                   if(!bhandled) {
                           throw_context.restore(); // restoring throw-context [exception were not handled]
                           rethrow;
                   }
           }
           return result;
   }
   
 // cache--  // cache--
   
 // consts  // consts
   
 const int DATA_STRING_SERIALIZED_VERSION=0x0004;  const int DATA_STRING_SERIALIZED_VERSION=0x0006;
   
 // helper types  // helper types
   
Line 417  void cache_delete(const String& file_spe Line 485  void cache_delete(const String& file_spe
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 class Cache_data: public PA_Object {  struct Cache_scope {
 public:  public:
         time_t expires;          time_t expires;
           const String* body_from_disk;
 };  };
 DECLARE_OBJECT_PTR(Cache_data);  
 struct Locked_process_and_cache_put_action_info {  struct Locked_process_and_cache_put_action_info {
         Request *r;          Request *r;
         Cache_data *data;          Cache_scope *scope;
         Value* body_code; const String& evaluated_body;          Value* body_code; Value* catch_code; 
           const String* processed_code;
 };  };
 #endif  #endif
   
   
   
   static StringOrValue process_cache_body_code(Request& r, Value* body_code) {
           return StringOrValue(r.process_to_string(*body_code));
   }
   
 /* @todo maybe network order worth spending some effort?  /* @todo maybe network order worth spending some effort?
         don't bothering myself with network byte order,          don't bothering myself with network byte order,
         am not planning to be able to move resulting file across platforms          am not planning to be able to move resulting file across platforms
Line 436  static void locked_process_and_cache_put Line 512  static void locked_process_and_cache_put
         Locked_process_and_cache_put_action_info& info=          Locked_process_and_cache_put_action_info& info=
                 *static_cast<Locked_process_and_cache_put_action_info *>(context);                  *static_cast<Locked_process_and_cache_put_action_info *>(context);
   
         =info.r->pool();  
           
         // body->process           // body->process 
         info.evaluated_body=info.r->process_to_string(info.body_code);          Try_catch_result result=try_catch(*info.r, 
                   process_cache_body_code, info.body_code,
                   info.catch_code);
   
           if(result.exception_should_be_handled) {
                   if(*result.exception_should_be_handled==CACHE_EXCEPTION_HANDLED_CACHE_NAME) {
                           if(const String* body_from_disk=info.scope->body_from_disk) // we have something old?
                                   info.processed_code=body_from_disk;
                           else
                                   throw Exception(0,
                                           new String("$"EXCEPTION_VAR_NAME"."EXCEPTION_HANDLED_PART_NAME"["CACHE_EXCEPTION_HANDLED_CACHE_NAME"]"),
                                           "fallback failed - there is no old cached body");
                   } else
                           throw Exception("parser.runtime",
                                   result.exception_should_be_handled,
                                   "$"EXCEPTION_VAR_NAME"."EXCEPTION_HANDLED_PART_NAME" value must be "
                                   "either boolean or string '"CACHE_EXCEPTION_HANDLED_CACHE_NAME"'");
           } else
                   info.processed_code=&result.processed_code.as_string();
   
         // expiration time not spoiled by ^cache(0) or something?          // expiration time not spoiled by ^cache(0) or something?
         if(info.data->expires > time(0)) {          if(info.scope->expires > time(0)) {
                 // string -serialize> buffer                  // string -serialize> buffer
                 char *data; size_t data_size;                  String::Cm serialized=info.processed_code->serialize(
                 info.evaluated_body->serialize(                          sizeof(Data_string_serialized_prolog));
                         sizeof(Data_string_serialized_prolog),   
                         data, data_size);  
                 Data_string_serialized_prolog& prolog=                  Data_string_serialized_prolog& prolog=
                         *reinterpret_cast<Data_string_serialized_prolog *>(data);                          *reinterpret_cast<Data_string_serialized_prolog *>(serialized.str);
                 prolog.version=DATA_STRING_SERIALIZED_VERSION;                  prolog.version=DATA_STRING_SERIALIZED_VERSION;
                 prolog.expires=info.data->expires;                  prolog.expires=info.scope->expires;
                                   
                 // buffer -write> file                  // buffer -write> file
                 write(f, data, data_size);                  write(f, serialized.str, serialized.length);
         } else // expired!          } else // expired!
                 info.data->expires=0; // flag it so that could be easily checked by caller                  info.scope->expires=0; // flag it so that could be easily checked by caller
 }  }
 const String& locked_process_and_cache_put(Request& r,   const String* locked_process_and_cache_put(Request& r, 
                                                                         Value* body_code,                                             Value& body_code,
                                                                         Cache_data& data,                                             Value* catch_code,
                                                                         const String& file_spec) {                                             Cache_scope& scope,
         Locked_process_and_cache_put_action_info info;                                             const String& file_spec) 
         info.r=&r;  {
         info.data=&data;          Locked_process_and_cache_put_action_info info={&r, &scope, &body_code, catch_code, 0};
         info.body_code=body_code;  
   
         const String& result=file_write_action_under_lock(          const String* result=file_write_action_under_lock(
                 file_spec,                   file_spec, 
                 "cache_put", locked_process_and_cache_put_action, &info,                  "cache_put", locked_process_and_cache_put_action, &info,
                 false/*as_text*/,                  false/*as_text*/,
                 false/*do_append*/,                  false/*do_append*/,
                 false/*block*/,                  false/*block*/,
                 false/*fail on lock problem*/) ? info.evaluated_body: 0;                  false/*fail on lock problem*/) ? info.processed_code: 0;
         time_t now=time(0);          time_t now=time(0);
         if(data.expires<=now)          if(scope.expires<=now)
                 cache_delete(file_spec);                  cache_delete(file_spec);
         return result;          return result;
 }  }
 const String& cache_getCharset& charset, const String& file_spec, time_t now) {  #ifndef DOXYGEN
         File_read_result file=file_read(charset, file_spec,   struct Cache_get_result {
           const String* body;
           bool expired;
   };
   #endif
   static Cache_get_result cache_get(Request_charsets& charsets, const String& file_spec, time_t now) {
           Cache_get_result result={0, false};
   
           File_read_result file=file_read(charsets, file_spec, 
                            false/*as_text*/,                              false/*as_text*/, 
                            0, //no params                             0, //no params
                            false/*fail_on_read_problem*/);                             false/*fail_on_read_problem*/);
         if(file.success && file.size/* ignore reads which are empty due to           if(file.success && file.length/* ignore reads which are empty due to 
                         non-unary open+lockEX conflict with lockSH */) {                          non-unary open+lockEX conflict with lockSH */) {
                                                   
                 Data_string_serialized_prolog& prolog=                  Data_string_serialized_prolog& prolog=
                         *reinterpret_cast<Data_string_serialized_prolog *>(file.data);                          *reinterpret_cast<Data_string_serialized_prolog *>(file.str);
   
                 const String& result(new String);                  String* body=new String;
                 if(                  if(
                         file.size>=sizeof(Data_string_serialized_prolog)                          file.length>=sizeof(Data_string_serialized_prolog)
                         && prolog.version==DATA_STRING_SERIALIZED_VERSION                          && prolog.version==DATA_STRING_SERIALIZED_VERSION) {
                         && prolog.expires > now                          if(body->deserialize(sizeof(Data_string_serialized_prolog),  file.str, file.length)) {
                         && result->deserialize(                                  result.body=body;
                                 sizeof(Data_string_serialized_prolog),  file.data, file.size, file_spec->cstr()))                                  result.expired=prolog.expires <= now;
                         return result;                          }
                   }
         }          }
   
         return 0;          return result;
 }  }
 static time_t as_expires(Request& r, const String& method_name, MethodParams* params,   static time_t as_expires(Request& r, MethodParams& params, 
                                                 int index, time_t now) {                                                  int index, time_t now) {
         time_t result;          time_t result;
         if(Value* vdate=(*params)[index]->as(VDATE_TYPE, false))          if(Value* vdate=params[index].as(VDATE_TYPE, false))
                 result=static_cast<VDate*>(vdate)->get_time();                  result=static_cast<VDate*>(vdate)->get_time();
         else          else
                 result=now+(time_t)params->as_double(index, "lifespan must be date or number", r);                  result=now+(time_t)params.as_double(index, "lifespan must be date or number", r);
                   
         return result;          return result;
 }  }
 static const String& as_file_spec(Request& r, MethodParams* params, int index) {  static const String& as_file_spec(Request& r, MethodParams& params, int index) {
         return r.absolute(params->as_string(index, "filespec must be string"));          return r.absolute(params.as_string(index, "filespec must be string"));
 }  }
 static void _cache(Request& r, const String& method_name, MethodParams* params) {  static void _cache(Request& r, MethodParams& params) {
         time_t now=time(0);          time_t now=time(0);
   
         // ^cache[filename] ^cache(seconds) ^cache[expires date]          // ^cache[filename] ^cache(seconds) ^cache[expires date]
         if(params->count()==1) {          if(params.count()==1) {
                 if((*params)[0]->is_string()) { // filename?                  if(params[0].is_string()) { // filename?
                         cache_delete(as_file_spec(r, params, 0));                          cache_delete(as_file_spec(r, params, 0));
                         return;                          return;
                 }                  }
   
                 // secods|expires date                  // secods|expires date
                 Cache_data* data=static_cast<Cache_data*>(r.classes_conf.get(cache_data_name).get());                  Cache_scope* scope=static_cast<Cache_scope*>(r.classes_conf.get(cache_data_name));
                 if(!data)                  if(!scope)
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 method_name,                                  0,
                                 "expire-time reducing instruction without cache");                                  "expire-time reducing instruction without cache");
                                   
                 time_t expires=as_expires(r, method_name, params, 0, now);                  time_t expires=as_expires(r, params, 0, now);
                 if(expires < data->expires)                  if(expires < scope->expires)
                         data->expires=expires;                          scope->expires=expires;
   
                 return;                  return;
         }          } else if(params.count()<3)
                   throw Exception("parser.runtime",
                           0,
                           "invalid number of parameters"); 
                   
         // file_spec, expires, body code          // file_spec, expires, body code
         const String& file_spec=r.absolute(params->as_string(0, "filespec must be string"));          const String& file_spec=r.absolute(params.as_string(0, "filespec must be string"));
   
           Cache_scope scope={as_expires(r, params, 1, now), 0};
   
         Cache_dataPtr data(new Cache_data);          Temp_hash_value<const String::Body, void*> 
         Temp_hash_value<String* , PA_Object*>                   cache_scope_setter(r.classes_conf, cache_data_name, &scope);
                 cache_data_setter(r.classes_conf, cache_data_name, data);          Value& body_code=params.as_junction(2, "body_code must be code");
         data->expires=as_expires(r, method_name, params, 1, now);          Value* catch_code=0;
         Value* body_code=params->as_junction(2, "body must be code");          if(params.count()>3)
                   catch_code=&params.as_junction(3, "catch_code must be code");
   
         if(data->expires>now) { // valid 'expires' specified? try cached copy...          if(scope.expires>now) { // valid 'expires' specified? try cached copy...
                 // hence we don't hope to have unary create/lockEX                  // hence we don't hope to have unary create/lockEX
                 // we need some plan to live in a life like that, so...                   // we need some plan to live in a life like that, so... 
                 // worst races plan:                  // worst races plan:
Line 566  static void _cache(Request& r, const Str Line 670  static void _cache(Request& r, const Str
                 //          |lockSH succeeds; ...                  //          |lockSH succeeds; ...
   
                 for(int retry=0; retry<2; retry++) {                  for(int retry=0; retry<2; retry++) {
                         if(const String& cached_body=cache_get(r.charsets.source(), file_spec, now)) { // have cached copy?                          Cache_get_result cached=cache_get(r.charsets, file_spec, now);
                                 // write it out                           if(cached.body) { // have cached copy
                                 r.write_assign_lang(*cached_body);                                  if(cached.expired) 
                                 // happy with it                                          scope.body_from_disk=cached.body; // storing for user to retrive it with ^cache[]
                                 return;                                  else // and it's not expired yet
                                   {
                                   // write it out 
                                   r.write_assign_lang(*cached.body);
                                   // happy with it
                                   return;
                           }
                         }                          }
   
                         // non-blocked lock; process; cache it                  // non-blocked lock; process; cache it
                         if(const String& processed_body=                  if(const String* processed_body=
                                 locked_process_and_cache_put(r, body_code, *data, file_spec)) {                          locked_process_and_cache_put(r, body_code, catch_code, scope, file_spec)) {
                                 // write it out                           // write it out 
                                 r.write_assign_lang(*processed_body);                          r.write_assign_lang(*processed_body);
                                 // happy with it                          // happy with it
                                 return;                          return;
                         } else { // somebody writing result right now                  } else { // somebody writing result right now
                                 pa_sleep(0, 500000); // waiting half a second                          pa_sleep(0, 500000); // waiting half a second
                                 retry=0; // prolonging our wait, than could cache_get it, without processing body_code                          retry=0; // prolonging our wait, than could cache_get it, without processing body_code
                         }                  }
                 }                  }
                 throw Exception(0,                  throw Exception(0,
                         file_spec,                          &file_spec,
                         "locking problem");                          "locking problem");
         } else {           } else { 
                 // instructed not to cache; forget cached copy                  // instructed not to cache; forget cached copy
Line 594  static void _cache(Request& r, const Str Line 704  static void _cache(Request& r, const Str
                 // process                  // process
                 const String& processed_body=r.process_to_string(body_code);                  const String& processed_body=r.process_to_string(body_code);
                 // write it out                   // write it out 
                 r.write_assign_lang(*processed_body);                  r.write_assign_lang(processed_body);
                 // happy with it                  // happy with it
                 return;                  return;
         }          }
         // never reached          // never reached
 }  }
   
 // also used in pa_request.C to pass param to @unhandled_exception  static StringOrValue process_try_body_code(Request& r, Value* body_code) {
 VHash* exception2vhashconst Exception& e) {          return r.process(*body_code);
         VHash* result(new VHash);  
         HashStringValue& hash=result->hash(0);  
         if(const char* type=e.type(true))  
                 hash.put(exception_type_part_name, Value*(new VString(String* (new String(type)))));  
         if(const String& asource=e.problem_source()) {  
                 const String* source(new String);   
                 source->append(*asource, String::L_TAINTED, true/*forced*/);  
   
                 hash.put(exception_source_part_name, Value*(new VString(source)));  
 #ifndef NO_STRING_ORIGIN  
                 const String_fragment::Origin& origin=source->origin();  
                 hash.put(String* (new String("file", 0, true)),  
                         Value*(new VString(String* (new String(origin.file)))));  
                 hash.put(String* (new String("lineno")),  
                         Value*(new VInt(1+origin.line)));  
 #endif  
         }  
         if(const char* ecomment=e.comment(true)) {  
                 char *pcomment=pool.copy(ecomment);  
                 hash.put(exception_comment_part_name,   
                         Value*(new VString(String* (new String(pcomment, 0, true/*tainted*/)))));  
         }  
         hash.put(exception_handled_part_name,   
                 Value*(new VBool(false)));  
   
         return result;  
 }  }
   static void _try_operator(Request& r, MethodParams& params) {
           Value& body_code=params.as_junction(0, "body_code must be code");
           Value& catch_code=params.as_junction(1, "catch_code must be code");
   
           Try_catch_result result=try_catch(r, 
                   process_try_body_code, &body_code,
                   &catch_code);
           
           if(result.exception_should_be_handled)
                   throw Exception("parser.runtime",
                           result.exception_should_be_handled,
                           "catch block must set $exception.handled to some boolean value, not string");
   
 static void _try_operator(Request& r, const String& method_name, MethodParams* params) {          // write out processed body_code or catch_code
           r.write_pass_lang(result.processed_code);
         Value* body_code=params->as_junction(0, "body_code must be code");  
         Value* catch_code=params->as_junction(1, "catch_code must be code");  
   
         StringOrValue result;  
         // taking snapshot of try-context  
         Request_context_saver try_context(r);  
         try {  
                 result=r.process(body_code);  
         } catch(const Exception& e) {  
                 Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error]  
                 try_context.restore(); // restoring try-context to perform catch-code  
   
                 VHash* vhash=exception2vhash(e);  
   
                 Junction* junction=catch_code->get_junction();  
                 Value* method_frame=junction->method_frame;  
                 Value* saved_exception_var_value=method_frame->get_element(exception_var_name, *method_frame, false);  
                 junction->method_frame->put_element(exception_var_name, vhash, false);  
                 result=r.process(catch_code);  
                 bool handled=false;  
                 if(Value* value=  
                         vhash->hash(0).get(exception_handled_part_name))  
                         handled=value->as_bool();                 
                 junction->method_frame->put_element(exception_var_name, saved_exception_var_value, false);  
   
                 if(!handled) {  
                         throw_context.restore(); // restoring throw-context [exception were not handled]  
                         throw(e); // rethrow  
                 }  
         }  
         // write out result  
         r.write_pass_lang(result);  
 }  }
   
 static void _throw_operator(Request& r, const String& method_name, MethodParams* params) {  static void _throw_operator(Request&, MethodParams& params) {
           if(params.count()==1) {
         if(params->count()==1) {                  if(HashStringValue *hash=params[0].get_hash()) {
                 Value* param0=(*params)[0];  
                 if(HashStringValue *hash=param0->get_hash(method_name)) {  
                         const char* type=0;                          const char* type=0;
                         if(Value* value=hash->get(exception_type_part_name))                          if(Value* value=hash->get(exception_type_part_name))
                                 type=value->as_string().cstr();                                  type=value->as_string().cstr();
                         const String* source;                          const String* source=0;
                         if(Value* value=hash->get(exception_source_part_name))                          if(Value* value=hash->get(exception_source_part_name))
                                 source=value->as_string();                                  source=&value->as_string();
                         const char* comment;                          const char* comment=0;
                         if(Value* value=hash->get(exception_comment_part_name))                          if(Value* value=hash->get(exception_comment_part_name))
                                 comment=value->as_string().cstr();                                  comment=value->as_string().cstr();
   
                         throw Exception(type,                          throw Exception(type,
                                 source?source:method_name,                                  source?source:0,
                                 "%s", comment?comment.get():"");                                  "%s", comment?comment:"");
                 } else                  } else
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 method_name,                                  0,
                                 "one-param version has hash param");                                  "one-param version has hash param");
         } else {          } else {
                 const char* type=params->as_string(0, "type must be string")->cstr();                  const char* type=params.as_string(0, "type must be string").cstr();
                 const String* source=params->as_string(1, "source must be string");                  const String& source=params.as_string(1, "source must be string");
                 const char* comment=params->count()>2? params->as_string(2, "comment must be string")->cstr()                  const char* comment=params.count()>2? params.as_string(2, "comment must be string").cstr()
                         :CharPtr(0);                          :0;
                 throw Exception(type, source, "%s", comment?comment.get():"");                  throw Exception(type, &source, "%s", comment?comment:"");
         }          }
 }   }
   
   static void _sleep_operator(Request& r, MethodParams& params) {
           double seconds=params.as_double(0, "seconds must be double", r);
           pa_sleep((int)trunc(seconds), (int)trunc(seconds*1000));
    }
   
 #if defined(WIN32) && defined(_DEBUG)  #if defined(WIN32) && defined(_DEBUG)
 #       define PA_BPT  #       define PA_BPT
 static void _bpt(Request& r, const String& method_name, MethodParams* params) {  static void _bpt(Request&, MethodParams&) {
         _asm int 3;          _asm int 3;
 }  }
 #endif  #endif
Line 708  static void _bpt(Request& r, const Strin Line 775  static void _bpt(Request& r, const Strin
 // constructor  // constructor
   
 VClassMAIN::VClassMAIN(): VClass() {  VClassMAIN::VClassMAIN(): VClass() {
         set_name(String* (new String(MAIN_CLASS_NAME)));          set_name(*new String(MAIN_CLASS_NAME));
   
 #ifdef PA_BPT  #ifdef PA_BPT
         // ^bpt[]          // ^bpt[]
Line 726  VClassMAIN::VClassMAIN(): VClass() { Line 793  VClassMAIN::VClassMAIN(): VClass() {
         add_native_method("taint", Method::CT_ANY, _taint, 1, 2);          add_native_method("taint", Method::CT_ANY, _taint, 1, 2);
   
         // ^process[code]          // ^process[code]
         add_native_method("process", Method::CT_ANY, _process, 1, 2);          add_native_method("process", Method::CT_ANY, _process, 1, 3);
   
         // ^rem{code}          // ^rem{code}
         add_native_method("rem", Method::CT_ANY, _rem, 1, 10000);          add_native_method("rem", Method::CT_ANY, _rem, 1, 10000);
Line 748  VClassMAIN::VClassMAIN(): VClass() { Line 815  VClassMAIN::VClassMAIN(): VClass() {
         add_native_method("connect", Method::CT_ANY, _connect, 2, 2);          add_native_method("connect", Method::CT_ANY, _connect, 2, 2);
   
   
           // ^cache[file_spec](time){code}[{catch code}] time=0 no cache
         // ^cache[file_spec] delete cache          // ^cache[file_spec] delete cache
         // ^cache[file_spec](time){code} time=0 no cache          add_native_method("cache", Method::CT_ANY, _cache, 1, 4);
         add_native_method("cache", Method::CT_ANY, _cache, 1, 3);  
                   
         // switch          // switch
   
Line 768  VClassMAIN::VClassMAIN(): VClass() { Line 835  VClassMAIN::VClassMAIN(): VClass() {
         // ^throw[type;source;comment]          // ^throw[type;source;comment]
         add_native_method("throw", Method::CT_ANY, _throw_operator, 1, 3);          add_native_method("throw", Method::CT_ANY, _throw_operator, 1, 3);
   
           add_native_method("sleep", Method::CT_ANY, _sleep_operator, 1, 1);
 }  }
   
 // constructor & configurator  // constructor & configurator
   
 VStateless_class* VClassMAIN_create() {  VStateless_class& VClassMAIN_create() {
                   return *new VClassMAIN;
         return VStateless_class*(new VClassMAIN);  
 }  }

Removed from v.1.127.2.22.2.8  
changed lines
  Added in v.1.153.2.2


E-mail: