Diff for /parser3/src/classes/op.C between versions 1.127.2.4 and 1.254

version 1.127.2.4, 2003/01/31 12:10:41 version 1.254, 2020/02/18 15:40:29
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-2017 Art. Lebedev Studio (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$";  
   
 #include "classes.h"  #include "classes.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_charset.h"
   
 // limits  volatile const char * IDENT_OP_C="$Id$";
   
 #define MAX_LOOPS 10000  
   
 // defines  // defines
   
Line 27  static const char* IDENT_OP_C="$Date$"; Line 28  static const char* IDENT_OP_C="$Date$";
   
 class VClassMAIN: public VClass {  class VClassMAIN: public VClass {
 public:  public:
         VClassMAIN(Pool& apool);          VClassMAIN();
 };  };
   
   // defines for statics
   
   #define SWITCH_DATA_NAME "SWITCH-DATA"
   #define CACHE_DATA_NAME "CACHE-DATA"
   
   #define EXCEPTION_VAR_NAME "exception"
   
   // statics
   
   //^switch ^case
   static const String switch_data_name(SWITCH_DATA_NAME);
   //^cache
   static const String cache_data_name(CACHE_DATA_NAME);
   
   static const String exception_var_name(EXCEPTION_VAR_NAME);
   
   
   // local defines
   
   #define CACHE_EXCEPTION_HANDLED_CACHE_NAME "cache"
   
 // helpers  // helpers
   
 Hash<StringPtr, int> Hash untaint_lang_name2enum;  class Untaint_lang_name2enum: public HashString<String::Language> {
   public:
           Untaint_lang_name2enum() {
                   #define ULN(name, LANG) put(name, (value_type)(String::L_##LANG));
                   ULN("clean", CLEAN);
                   ULN("as-is", AS_IS);
                   ULN("optimized-as-is", AS_IS|String::L_OPTIMIZE_BIT);
                   ULN("file-spec", FILE_SPEC);
                   ULN("http-header", HTTP_HEADER);
                   ULN("mail-header", MAIL_HEADER);
                   ULN("uri", URI);
                   ULN("sql", SQL);
                   ULN("js", JS);
                   ULN("xml", XML);
                   ULN("optimized-xml", XML|String::L_OPTIMIZE_BIT);
                   ULN("html", HTML);
                   ULN("optimized-html", HTML|String::L_OPTIMIZE_BIT);
                   ULN("regex", REGEX);
                   ULN("parser-code", PARSER_CODE);
                   ULN("json", JSON);
                   #undef ULN
           }
   } untaint_lang_name2enum;
   
 // methods  // methods
   
 static void _if(Request& r, const String&, MethodParams *params) {  static void _if(Request& r, MethodParams& params) {
         Value& condition_code=params->as_junction(0, "condition must be expression");          size_t max_param=params.count()-1;
           size_t i=0;
           do {
                   bool condition=params.as_bool(i, "condition must be expression", r);
                   if(condition) {
                           r.process_write(params[i+1]);
                           return;
                   }
                   i+=2;
           } while (i < max_param);
   
         bool condition=r.process_to_value(condition_code,           if(i == max_param)
                 /*0/*no name* /,*/                  r.process_write(params[i]);
                 false/*don't intercept string*/).as_bool();  }
         if(condition)  
                 r.write_pass_lang(r.process(params->as_junction(1, "'then' parameter must be code")));  String::Language get_untaint_lang(const String& lang_name){
         else if(params->size()>2)          String::Language lang=untaint_lang_name2enum.get(lang_name);
                 r.write_pass_lang(r.process(params->as_junction(2, "'else' parameter must be code")));          if(!lang)
 }                  throw Exception(PARSER_RUNTIME, &lang_name, "invalid taint language");
           return lang;
 static void _untaint(Request& r, const String& method_name, MethodParams *params) {  }
         Pool& pool=r.pool();  
   static void _untaint(Request& r, MethodParams& params) {
         uchar lang;          String::Language lang;
         if(params->size()==1)          if(params.count()==1)
                 lang=String::UL_AS_IS; // mark as simply 'tainted'. useful in html from sql                   lang=String::L_AS_IS; // mark as simply 'as-is'. useful in html from sql
         else {          else
                 const String& lang_name=params->as_string(0, "lang must be string");                  lang=get_untaint_lang(params.as_string(0, "lang must be string"));
                 lang=untaint_lang_name2enum->get_int(lang_name);  
                 if(!lang)          Value& vbody=params.as_junction(params.count()-1, "body must be code");
                         throw Exception(0,          Value& result=r.process(vbody);
                                 &lang_name,  
                                 "invalid taint language");          if(const String* string=result.get_string()){
         }                  String &untainted=*new String();
                   string->append_to(untainted, lang); // mark all tainted to specified language
                   r.write(untainted);
           } else
                   r.write(result); // this is not normal, just backward compatibility
   }
   
   static void _taint(Request& r, MethodParams& params) {
           String::Language lang;
           if(params.count()==1)
                   lang=String::L_TAINTED; // mark as simply 'tainted'. useful in table:create
           else
                   lang=get_untaint_lang(params.as_string(0, "lang must be string"));
   
         {          {
                 Value& vbody=params->as_junction(params->size()-1, "body must be code");                  Value& vbody=params.as_no_junction(params.count()-1, "body must not be code");
                                   
                 Temp_lang temp_lang(r, lang); // set temporarily specified ^untaint[language;                  String result(vbody.as_string(), lang); // force result language to specified
                 r.write_pass_lang(r.process(vbody)); // process marking tainted with that lang                  r.write(result);
         }          }
 }  }
   
 static void _taint(Request& r, const String&, MethodParams *params) {  static void _apply_taint(Request& r, MethodParams& params) {
         Pool& pool=r.pool();          String::Language lang=params.count()==1 ? String::L_AS_IS : get_untaint_lang(params.as_string(0, "lang must be string"));
           const String &sbody=params.as_string(params.count()-1, "body must be string");
           String::Body result_body=sbody.cstr_to_string_body_untaint(lang, r.connection(false), &r.charsets);
           r.write(*new String(result_body, String::L_AS_IS));
   }
   
   static void _process(Request& r, MethodParams& params) {
           Method* main_method;
   
         uchar lang;          size_t index=0;
         if(params->size()==1)          Value* target_self;
                 lang=String::UL_TAINTED; // mark as simply 'tainted'. useful in table:set          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 {          else {
                 const String& lang_name=params->as_string(0, "lang must be string");                  target_self=&maybe_target_self;
                 lang=untaint_lang_name2enum->get_int(lang_name);                  if(params.count()==1)
                 if(!lang)                          throw Exception(PARSER_RUNTIME, 0, "no body specified");
                         throw Exception(0,                  index++;
                                 &lang_name,  
                                 "invalid taint language");  
         }          }
   
         {          {
                 Value& vbody=params->as_no_junction(params->size()-1, "body must not be code");                  VStateless_class *target_class=target_self->get_class();
                                   if(!target_class)
                 String& result=*new(pool) String(pool);                          throw Exception(PARSER_RUNTIME, 0, "no target class");
                 result.append(  
                         vbody.as_string(),  // process marking tainted with that lang  
                         lang, true);  // force result language to specified  
                 r.write_pass_lang(result);  
         }  
 }  
   
 static void _process(Request& r, const String& method_name, MethodParams *params) {                  // temporary zero @main so to maybe-replace it in processed code
         Pool& pool=r.pool();                  Temp_method temp_method_main(*target_class, main_method_name, 0);
         const Method *main_method;  
         Value& target_self=params->size()>1?  
                 params->as_no_junction(0, "target must not be code")  
                 :r.get_method_frame()->caller()->self();  
         {  
                 Value& vjunction=params->as_junction(params->size()-1, "body must be code");  
   
                 VStateless_class *target_class=target_self.get_last_derived_class();                  const String* main_alias=0;
                 if(!target_class)                  const String* file_alias=0;
                         throw Exception("parser.runtime",                  int line_no_alias_offset=0;
                                 &method_name,                  bool allow_class_replace=false;
                                 "no target class");  
                                   size_t options_index=index+1;
                   if(options_index<params.count())
                           if(HashStringValue* options=params.as_hash(options_index)) {
   
                                   int valid_options=0;
                                   for(HashStringValue::Iterator i(*options); i; i.next() ){
   
                                           String::Body key=i.key();
                                           Value* value=i.value();
   
                                           if(key == "main") {
                                                   valid_options++;
                                                   main_alias=&value->as_string();
                                           } else if(key == "file") {
                                                   valid_options++;
                                                   file_alias=&value->as_string();
                                           } else if(key == "lineno") {
                                                   valid_options++;
                                                   line_no_alias_offset=value->as_int();
                                           } else if(key == "replace") {
                                                   valid_options++;
                                                   allow_class_replace=r.process(*value).as_bool();
                                           }
                                   }
   
                                   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                  // evaluate source to process
                 const String& source=r.process_to_string(vjunction);                  const String& source=r.process_to_string(vjunction);
   
                 // temporary remove language change                  Temp_class_replace class_replace(r, allow_class_replace);
                 Temp_lang temp_lang(r, String::UL_PASS_APPENDED);  
                 // temporary zero @main so to maybe-replace it in processed code                  r.use_buf(*target_class, source.untaint_cstr(String::L_PARSER_CODE, r.connection(false)), main_alias, processe_file_no, line_no_alias_offset);
                 Temp_method temp_method_main(*target_class, r.main_method_name, 0);  
                 // temporary zero @auto so it wouldn't be auto-called in Request::use_buf  
                 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 Origin& source_origin=source.origin();  
                 const 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())+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=(char *)r.malloc(place_size);  
                 memcpy(heap_place, local_place, place_size);  
   
                 // process source code, append processed methods to 'self' class  
                 // maybe-define new @main  
                 r.use_buf(*target_class,  
                         source.cstr(String::UL_UNSPECIFIED, r.connection(0)),   
                         *new(pool) String(pool, heap_place, place_size, true /*tainted*/),  
                         heap_place);  
                   
                 // main_method                  // main_method
                 main_method=target_class->get_method(r.main_method_name);                  main_method=target_class->get_method(main_method_name);
         }          }
         // after restoring current-request-lang          // after restoring current-request-lang
         // maybe-execute @main[]          // maybe-execute @main[]
         if(main_method) {          if(main_method) {
                 // temporarily set method_frame's self to target_self                  METHOD_FRAME_ACTION(*main_method, r.get_method_frame()->caller(), *target_self, {
                 Temp_method_frame_self tmfs(*r.get_method_frame(), target_self);                          frame.empty_params();
                 // execute!                               r.call(frame);
                 r.execute(*main_method->parser_code);                          r.write(frame.result());
                   });
         }          }
 }  }
                   
 static void _rem(Request& r, const String&, 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) {
         Pool& pool=r.pool();          InCycle temp(r);
   
           Value& vcondition=params.as_expression(0, "condition must be number, bool or expression");
   
         Value& vcondition=params->as_junction(0, "condition must be expression");          Value& body_code=params.as_junction(1, "body must be code");
         Value& body=params->as_junction(1, "body must be code");          Value* delim_maybe_code=params.count()>2?&params[2]:0;
   
         // while...          // while...
         int endless_loop_count=0;          int endless_loop_count=0;
         while(true) {          if(delim_maybe_code){ // delimiter set
                 if(++endless_loop_count>=MAX_LOOPS) // endless loop?                  bool need_delim=false;
                         throw Exception("parser.runtime",                  while(true) {
                                 &method_name,                          if(++endless_loop_count>=pa_loop_limit) // endless loop?
                                 "endless loop detected");                                  throw Exception(PARSER_RUNTIME, 0, "endless loop detected");
   
                 bool condition=r.process_to_value(vcondition,                           if(!r.process(vcondition).as_bool())
                                 /*0/*no name* /,*/                                  break;
                                 false/*don't intercept string*/).as_bool();  
                 if(!condition) // ...condition is true                          Value& sv_processed=r.process(body_code);
                         break;                          TempSkip4Delimiter skip(r);
   
                           const String* s_processed=sv_processed.get_string();
                           if(s_processed && !s_processed->is_empty()) { // we have body
                                   if(need_delim) // need delim & iteration produced string?
                                           r.write(r.process(*delim_maybe_code));
                                   else
                                           need_delim=true;
                           }
                           r.write(sv_processed);
   
                           if(skip.check_break())
                                   break;
                   }
           } else {
                   while(true) {
                           if(++endless_loop_count>=pa_loop_limit) // endless loop?
                                   throw Exception(PARSER_RUNTIME, 0, "endless loop detected");
   
                 // write processed body                          if(!r.process(vcondition).as_bool())
                 r.write_pass_lang(r.process(body));                                  break;
   
                           r.process_write(body_code);
   
                           if(r.check_skip_break())
                                   break;
                   }
         }          }
 }  }
   
 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());  
           bool allow_class_replace=false;
           const String* use_origin=0;
   
           if(params.count()==2)
                   if(HashStringValue* options=params.as_hash(1)) {
                           int valid_options=0;
                           for(HashStringValue::Iterator i(*options); i; i.next() ){
   
                                   String::Body key=i.key();
                                   Value* value=i.value();
   
                                   if(key == "replace") {
                                           valid_options++;
                                           allow_class_replace=r.process(*value).as_bool();
                                   }
   
                                   if(key == "origin") {
                                           valid_options++;
                                           use_origin=&value->as_string();
                                   }
   
                                   if(valid_options!=options->count())
                                           throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                           }
                   }
   
           if(!use_origin)
                   if(VMethodFrame* caller=r.get_method_frame()->caller())
                           use_origin=r.get_method_filespec(&caller->method);
   
           Temp_class_replace class_replace(r, allow_class_replace);
   
           r.use_file(r.main_class, vfile.as_string(), use_origin);
   }
   
   static void set_skip(Request& r, Request::Skip askip) {
           if(!r.get_in_cycle())
                   throw Exception(askip==Request::SKIP_BREAK ? "parser.break" : "parser.continue", 0, "without cycle");
           r.set_skip(askip);
   }
   
   static void _break(Request& r, MethodParams& params) {
           if(!params.count() || params.as_bool(0, "condition must be expression", r)) set_skip(r, Request::SKIP_BREAK);
 }  }
   
 static void _for(Request& r, const String& method_name, MethodParams *params) {  static void _continue(Request& r, MethodParams& params) {
         Pool& pool=r.pool();          if(!params.count() || params.as_bool(0, "condition must be expression", r)) set_skip(r, Request::SKIP_CONTINUE);
         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 to=params->as_int(2, "to must be int", r);  static void _return(Request& r, MethodParams& params) {
         Value& body_code=params->as_junction(3, "body must be code");          VMethodFrame& caller=*r.get_method_frame()->caller();
         Value *delim_maybe_code=params->size()>4?&params->get(4):0;          if(params.count())
                   r.put_element(caller, Symbols::RESULT_SYMBOL, &params[0]);
           r.set_skip_return(caller);
   }
   
   static void _for(Request& r, MethodParams& params) {
           InCycle temp(r);
   
           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 to=params.as_int(2, "to must be int", r);
           Value& body_code=params.as_junction(3, "body must be code");
           Value* delim_maybe_code=params.count()>4?&params[4]:0;
   
           if(to-from>=pa_loop_limit) // too long loop?
                   throw Exception(PARSER_RUNTIME, 0, "endless loop detected");
   
         if(to-from>=MAX_LOOPS) // too long loop?          VInt* vint=new VInt(0);
                 throw Exception("parser.runtime",  
                         &method_name,  
                         "endless loop detected");  
   
         bool need_delim=false;          VMethodFrame& caller=*r.get_method_frame()->caller();
         VInt *vint=new(pool) VInt(pool, 0);          r.put_element(caller, var_name, vint);
         r.get_method_frame()->caller()->put_element(var_name, vint, false);          if(delim_maybe_code){ // delimiter set 
         for(int i=from; i<=to; i++) {                  bool need_delim=false;
                 vint->set_int(i);  
   
                 StringOrValue sv_processed=r.process(body_code);                  for(int i=from; i<=to; i++) {
                 const String *s_processed=sv_processed.get_string();                          vint->set_int(i);
                 if(delim_maybe_code && s_processed && s_processed->size()) { // delimiter set and we have body  
                         if(need_delim) // need delim & iteration produced string?                          Value& sv_processed=r.process(body_code);
                                 r.write_pass_lang(r.process(*delim_maybe_code));                          TempSkip4Delimiter skip(r);
                         need_delim=true;  
                           const String* s_processed=sv_processed.get_string();
                           if(s_processed && !s_processed->is_empty()) { // we have body
                                   if(need_delim) // need delim & iteration produced string?
                                           r.write(r.process(*delim_maybe_code));
                                   else
                                           need_delim=true;
                           }
                           r.write(sv_processed);
   
                           if(skip.check_break())
                                   break;
                   }
           } else {
                   for(int i=from; i<=to; i++) {
                           vint->set_int(i);
    
                           r.process_write(body_code);
   
                           if(r.check_skip_break())
                                   break;
                 }                  }
                 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(expr).as_expr_result();
                 /*0/*no name YET* /,*/          if(params.count()>1) {
                 true/*don't intercept string*/).as_expr_result();                  const String& fmt=params.as_string(1, "fmt must be string").trim();
         if(params->size()>1) {                  if(fmt.is_empty()){
                 Value& fmt=params->as_no_junction(1, "fmt must not be code");                          r.write(value_result);
                   } else {
                 Pool& pool=r.pool();                          r.write(String(format(value_result.as_double(), fmt.cstrm())));
                 String& string=*new(pool) String(pool);                  }
                 string.APPEND_CONST(format(pool, value_result->as_double(), fmt.as_string().cstr()));  
                 r.write_no_lang(string);  
         } else          } else
                 r.write_no_lang(*value_result);                  r.write(value_result);
 }  }
   
 static void _connect(Request& r, const String& method_name, MethodParams *params) {  static void _connect(Request& r, MethodParams& params) {
         Pool& pool=r.pool();          Value& url=params.as_no_junction(0, "url must not be code");
 #ifdef RESOURCES_DEBUG          Value& body_code=params.as_junction(1, "body must be code");
 struct timeval mt[2];  
 #endif  
         Value& url=params->as_no_junction(0, "url must not 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(*new(pool) String(pool, MAIN_SQL_NAME), r.main_class, false)) {          if(Value* sql=r.main_class.get_element(String(MAIN_SQL_NAME))) {
                 if(Value *element=sql->get_element(*new(pool) String(pool, MAIN_SQL_DRIVERS_NAME), *sql, false)) {                  if(Value* element=sql->get_element(String(MAIN_SQL_DRIVERS_NAME))) {
                         protocol2driver_and_client=element->get_table();                          protocol2driver_and_client=element->get_table();
                 }                  }
         }          }
   
 #ifdef RESOURCES_DEBUG  
 //measure:before  
 gettimeofday(&mt[0],NULL);  
 #endif  
         // connect          // connect
         SQL_Connection_ptr connection=SQL_driver_manager->get_connection(          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(),
                   r.request_info.document_root);
   
 #ifdef RESOURCES_DEBUG          Temp_connection temp_connection(r, connection);
 //measure:after connect  
 gettimeofday(&mt[1],NULL);  
   
 double t[2];  
 for(int i=0;i<2;i++)  
     t[i]=mt[i].tv_sec+mt[i].tv_usec/1000000.0;  
   
 r.sql_connect_time+=t[1]-t[0];  
 #endif  
         Temp_connection temp_connection(r, connection.get());  
         // execute body          // execute body
         try {          try {
                 r.write_assign_lang(r.process(body_code));                  r.process_write(body_code);
                   connection->commit();
                   connection->close();
         } catch(...) { // process problem          } catch(...) { // process problem
                 connection->mark_to_rollback();                  connection->rollback();
                 /*re*/throw;                   connection->close();
                   rethrow;
         }          }
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Switch_data {  class Switch_data: public PA_Object {
         Request *r;  public:
         Value *searching;          Request& r;
         Value *found;          const String* searching_string;
         Value *_default;          double searching_double;
           bool searching_bool;
           Value* found;
           Value* _default;
   public:
           Switch_data(Request& ar, Value& asearching): 
                   r(ar)
           {
                   if(asearching.is_string() || asearching.is_void()){
                           searching_string=&asearching.as_string();
                           searching_double=0;
                           searching_bool=false;
                   } else {
                           searching_string=0;
                           searching_double=asearching.as_double();
                           searching_bool=asearching.is_bool();
                   }
           }
 };  };
 #endif  #endif
 static void _switch(Request& r, const String&, MethodParams *params) {  static void _switch(Request& r, MethodParams& params) {
         Switch_data data={&r, &r.process_to_value(params->get(0))};               Switch_data* data=new Switch_data(r, r.process(params[0]));
         Temp_hash_value switch_data_setter(r.classes_conf, *switch_data_name, &data);          Temp_hash_value<HashString<void*>, void*> 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);
         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.process_write(*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) {
         Pool& pool=r.pool();          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));  
         if(!data)          if(!data)
                 throw Exception("parser.runtime",                  throw Exception(PARSER_RUNTIME,
                         &method_name,                          0,
                         "without switch");                          "without switch");
   
         int count=params->size();          if(data->found) // matches already was found
         Value *code=&params->as_junction(--count, "case result must be code");                  return;
           
         // 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++) {          int count=params.count();
                 Value& value=r.process_to_value(params->get(i));          Value* code=&params.as_expression(--count, "case result must be code");
   
                 if(value.as_string() == CASE_DEFAULT_VALUE) {  #ifdef USE_DESTRUCTORS
           Junction *j=code->get_junction();
           if (j){
                   code=new VJunction(j->self,j->method,j->method_frame,j->rcontext,j->wcontext,j->code);
                   if (j->wcontext) j->wcontext->attach_junction((VJunction *)code);
           }
   #endif
           
           for(int i=0; i<count; i++){
                   Value& value=r.process(params[i]);
   
                   if(value.is_string() && value.as_string() == CASE_DEFAULT_VALUE){
                         data->_default=code;                          data->_default=code;
                         break;                          continue;
                 }                  }
   
                 bool matches;                  bool matches;
                 if(data->searching->is_string())                  if(data->searching_string)
                         matches=data->searching->as_string() == value.as_string();                          matches=(*data->searching_string) == value.as_string();
                   else if(data->searching_bool || value.is_bool())
                           matches=(data->searching_double != 0) == value.as_bool();
                 else                  else
                         matches=data->searching->as_double() == value.as_double();                          matches=data->searching_double == value.as_double();
   
                 if(matches) {  
                         if(data->found)  
                                 throw Exception("parser.runtime",  
                                         &method_name,  
                                         "duplicate found");  
   
                   if(matches){
                         data->found=code;                          data->found=code;
                         break;                          break;
                 }                  }
         }          }
 }  }
   #ifndef DOXYGEN
   struct Try_catch_result {
           ValueRef processed_code;
           const String* exception_should_be_handled;
   
           Try_catch_result(): exception_should_be_handled(0) {}
   };
   
   /// Auto-object used for temporary changing Request::skip.
   class Temp_skip {
           Request& frequest;
           Request::Skip saved_skip;
   public:
           Temp_skip(Request& arequest) : frequest(arequest), saved_skip(arequest.get_skip()) {
                   arequest.set_skip(Request::SKIP_NOTHING);
           }
           ~Temp_skip() {
                   if(frequest.get_skip() == Request::SKIP_NOTHING)
                           frequest.set_skip(saved_skip);
           }
   };
   #endif
   
   /// used by ^try and ^cache, @returns $exception.handled[string] if any
   template<class I>
   static Try_catch_result try_catch(Request& r, Value& body_code(Request&, I), I info, Value* catch_code, bool could_be_handled_by_caller=false) {
           Try_catch_result result;
   
           // minor bug: context not restored if only finally code is present, see #1062
           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); // remembering exception stack trace
   
                   Request::Exception_details details=r.get_details(e);
   
                   try_context.restore(); // restoring try-context for code after try and catch-code
   
                   {
                           Temp_value_element temp(r, *catch_code->get_junction()->method_frame, exception_var_name, &details.vhash);
                           Temp_skip temp_skip(r);
                           result.processed_code=r.process(*catch_code);
                   }
                   
                   // retriving $exception.handled
                   Value* vhandled=details.vhash.hash().get(exception_handled_part_name);
   
                   bool bhandled=false;
                   if(vhandled) {
                           if(vhandled->is_string()) { // not simple $exception.handled(1/0)?
                                   if(bhandled=could_be_handled_by_caller) { // and we can possibly handle it
                                           result.exception_should_be_handled=vhandled->get_string(); // considering 'recovered' and let the caller recover
                                   }
                           } else
                                   bhandled=vhandled->as_bool();
                   }
   
                   if(!bhandled){
                           throw_context.restore(); // restoring exception stack trace creared by try_context.restore()
                           rethrow;
                   }
           }
   
           return result;
   }
   
   static Value& process_try_body_code(Request& r, Value* body_code) {
           return r.process(*body_code);
   }
   
 // cache--  // cache--
   
 // consts  // consts
   
 const int DATA_STRING_SERIALIZED_VERSION=0x0003;  const int DATA_STRING_SERIALIZED_VERSION=0x0006;
   
 // helper types  // helper types
   
Line 368  struct Data_string_serialized_prolog { Line 605  struct Data_string_serialized_prolog {
 #endif  #endif
   
 void cache_delete(const String& file_spec) {  void cache_delete(const String& file_spec) {
         file_delete(file_spec, false/*fail_on_read_problem*/);          file_delete(file_spec, false/*fail_on_problem*/);
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Cache_data {  struct Cache_scope {
   public:
         time_t expires;          time_t expires;
           const String* body_from_disk;
 };  };
 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 Value& process_cache_body_code(Request& r, Value* body_code) {
           return r.process(*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 388  struct Locked_process_and_cache_put_acti Line 636  struct Locked_process_and_cache_put_acti
 static void locked_process_and_cache_put_action(int f, void *context) {  static void locked_process_and_cache_put_action(int f, void *context) {
         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);
           
           const String* body_from_disk=info.scope->body_from_disk;
         // 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, body_from_disk!=0 /*we have something old=we can handle=recover later*/);
   
           if(result.exception_should_be_handled) {
                   if(*result.exception_should_be_handled==CACHE_EXCEPTION_HANDLED_CACHE_NAME) {
                           assert(body_from_disk);
                           info.processed_code=body_from_disk;
                   } 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=&((Value &)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
                 void *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=
                         *static_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) 
                 &r,  {
                 &data,          Locked_process_and_cache_put_action_info info={&r, &scope, &body_code, catch_code, 0};
                 &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 == don't wait till other thread release lock*/,
                 false/*fail on lock problem*/) ? info.evaluated_body: 0;                  false/*dun throw exception if lock failed*/) ? 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;
 }  }
 String *cache_get(Pool& pool, const String& file_spec, time_t now) {  
         void* data; size_t data_size;  
         if(file_read(pool, file_spec,   
                            data, data_size,   
                            false/*as_text*/,   
                            0, 0, //no params&out_fields  
                            false/*fail_on_read_problem*/)  
             && data_size/* ignore reads which are empty due to   
                         non-unary open+lockEX conflict with lockSH */) {  
           
                 Data_string_serialized_prolog& prolog=  
                         *static_cast<Data_string_serialized_prolog *>(data);  
   
                 String *result=new(pool) String(pool);  #ifndef DOXYGEN
                 if(  struct Cache_get_result {
                         data_size>=sizeof(Data_string_serialized_prolog)          const String* body;
                         && prolog.version==DATA_STRING_SERIALIZED_VERSION          bool expired;
                         && prolog.expires > now  };
                         && result->deserialize(  #endif
                                 sizeof(Data_string_serialized_prolog),  data, data_size, file_spec.cstr()))  
                         return result;  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_binary(file_spec, false /*fail_on_read_problem*/);
           if(file.success && file.length /*ignore reads which are empty due to non-unary open+lockEX conflict with lockSH*/) {
                   
                   Data_string_serialized_prolog& prolog = *reinterpret_cast<Data_string_serialized_prolog *>(file.str);
                   
                   String* body=new String;
                   if(file.length>=sizeof(Data_string_serialized_prolog) && prolog.version==DATA_STRING_SERIALIZED_VERSION) {
                           if(body->deserialize(sizeof(Data_string_serialized_prolog),  file.str, file.length)) {
                                   result.body=body;
                                   result.expired=prolog.expires <= now;
                           }
                   }
         }          }
   
         return 0;          return result;
 }  }
 static time_t as_expires(Request& r, const String& method_name, MethodParams *params,   
                                                 int index, time_t now) {  static time_t as_expires(Request& r, MethodParams& params, int index, time_t now) {
         time_t result;          time_t result;
         Value& vlifespan_or_expires=params->get(index);          if(Value* vdate=params[index].as(VDATE_TYPE))
         if(Value *vdate=vlifespan_or_expires.as(VDATE_TYPE, false))                  result=(time_t)(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) {  
         return r.absolute(params->as_string(index, "filespec must be string"));  static const String& as_file_spec(Request& r, MethodParams& params, int index) {
           return r.absolute(params.as_string(index, "filespec must be string"));
 }  }
 static void _cache(Request& r, const String& method_name, MethodParams *params) {  
         Pool& pool=r.pool();  static void _cache(Request& r, MethodParams& params) {
           if(params.count()==0) {
                   // ^cache[] -- return current expiration time
                   Cache_scope* scope=static_cast<Cache_scope*>(r.classes_conf.get(cache_data_name));
                   if(!scope)
                           throw Exception(PARSER_RUNTIME, 0, "expire-time get without cache");
                   r.write(*new VDate((pa_time_t)scope->expires));
                   return;
           }
   
         time_t now=time(0);          time_t now=time(0);
   
         // ^cache[filename] ^cache(seconds) ^cache[expires date]          if(params.count()==1) {
         if(params->size()==1) {                  // ^cache[filename] ^cache(seconds) ^cache[expires date]
                 if(params->get(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));                  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=as_file_spec(r, params, 0);
   
         Cache_data data;          Cache_scope scope={as_expires(r, params, 1, now), 0};
         Temp_hash_value cache_data_setter(r.classes_conf, *cache_data_name, &data);  
         data.expires=as_expires(r, method_name, params, 1, now);  
         Value& body_code=params->as_junction(2, "body must be code");  
   
         if(data.expires>now) { // valid 'expires' specified? try cached copy...  
                 // hence we don't hope to have unary create/lockEX  
                 // we need some plan to live in a life like that, so...   
                 // worst races plan:  
                 // A        B  
                 // open  
                 //          |open  
                 // lockSH  
                 //          |nonblocking-lockEX fails  
                 // unlockSH  
                 // close, cache_get returns 0  
                 // open  
                 // nonblocking-lockEX succeeds; process, write, close  
                 //          |retry1: open  
                 // ...  
                 //          |lockSH succeeds; ...  
   
                 for(int retry=0; retry<2; retry++) {  
                         if(String *cached_body=cache_get(pool, file_spec, now)) { // have cached copy?  
                                 // write it out   
                                 r.write_assign_lang(*cached_body);  
                                 // happy with it  
                                 return;  
                         }  
   
                         // non-blocked lock; process; cache it          Temp_hash_value<HashString<void*>, void*> cache_scope_setter(&r.classes_conf, cache_data_name, &scope);
                         if(const String*processed_body=          Value& body_code=params.as_junction(2, "body_code must be code");
                                 locked_process_and_cache_put(r, body_code, data, file_spec)) {          Value* catch_code=0;
                                 // write it out           if(params.count()>3)
                                 r.write_assign_lang(*processed_body);                  catch_code=&params.as_junction(3, "catch_code must be code");
                                 // happy with it  
                                 return;          if(scope.expires>now) {
                         } else { // somebody writing result right now                  Cache_get_result cached=cache_get(r.charsets, file_spec, now);
                                 pa_sleep(0, 500000); // waiting half a second  
                                 retry=0; // prolonging our wait, than could cache_get it, without processing body_code                  if(cached.body) { // have cached copy
                         }                          if(cached.expired) {
                                   scope.body_from_disk=cached.body; // storing for user to retrive it with ^cache[]
                           } else {
                                   // and it's not expired yet write it out 
                                   r.write(*cached.body);
                                   // happy with it
                                   return;
                           }
                   }
   
                   // no cached info or it's already expired
   
                   // trying to process it under lock and store result in file
                   const String* processed_body=locked_process_and_cache_put(r, body_code, catch_code, scope, file_spec);
                   if(processed_body){
                           // write it out 
                           r.write(*processed_body);
                           // happy with it
                           return;
                   } else {
                           // we fail while get exclusive lock. nvm, we just execute body_code a bit later
                 }                  }
                 throw Exception(0,  
                         &file_spec,  
                         "locking problem");  
         } else {           } else { 
                 // instructed not to cache; forget cached copy                  // instructed not to cache; forget cached copy
                 cache_delete(file_spec);                  cache_delete(file_spec);
                 // process  
                 const String& processed_body=r.process_to_string(body_code);  
                 // write it out   
                 r.write_assign_lang(processed_body);  
                 // happy with it  
                 return;  
         }          }
         // never reached          
           // process without caching
           if(catch_code){
                   Try_catch_result result=try_catch(r, process_try_body_code, &body_code, catch_code);
                   r.write(result.processed_code);
           } else {
                   r.write(r.process_to_string(body_code));
           }
 }  }
   
 // also used in pa_request.C to pass param to @unhandled_exception  static void _try_operator(Request& r, MethodParams& params) {
 VHash& exception2vhash(Pool& pool, const Exception& e) {          Value& body_code=params.as_junction(0, "body_code must be code");
         VHash& result=*new(pool) VHash(pool);          Value& catch_code=params.as_junction(1, "catch_code must be code");
         Hash& hash=result.hash(0);          Value* finally_code=(params.count()==3) ? &params.as_junction(2, "finally_code must be code") : 0;
         if(const char *type=e.type(true))  
                 hash.put(*exception_type_part_name, new(pool) VString(*new(pool) String(pool, type)));  
         if(const String *asource=e.problem_source()) {  
                 String& source=*new(pool) String(pool);   
                 source.append(*asource, String::UL_TAINTED, true/*forced*/);  
   
                 hash.put(*exception_source_part_name, new(pool) VString(source));  
 #ifndef NO_STRING_ORIGIN  
                 const Origin& origin=source.origin();  
                 hash.put(*new(pool) String(pool, "file", 0, true),  
                         new(pool) VString(*new(pool) String(pool, origin.file)));  
                 hash.put(*new(pool) String(pool, "lineno"),  
                         new(pool) VInt(pool, 1+origin.line));  
 #endif  
         }  
         if(const char *ecomment=e.comment(true)) {  
                 int comment_size=strlen(ecomment);  
                 char *pcomment=(char *)pool.malloc(comment_size);  
                 memcpy(pcomment, ecomment, comment_size);  
                 hash.put(*exception_comment_part_name,   
                         new(pool) VString(*new(pool) String(pool, pcomment, comment_size, true/*tainted*/)));  
         }  
         hash.put(*exception_handled_part_name,   
                 new(pool) VBool(pool, false));  
   
         return result;          Try_catch_result result;
 }  
           try{
                   // process try and catch code
                   result=try_catch(r, process_try_body_code, &body_code, &catch_code);
           } catch(...){
                   // process finally code but ignore the result
                   if(finally_code){
                           Temp_skip temp(r);
                           /* Value &finally_result= */ r.process(*finally_code);
                   }
                   rethrow;
           }
   
 static void _try_operator(Request& r, const String& method_name, MethodParams *params) {          // process finally code
         Pool& pool=r.pool();          if(finally_code){
                   Temp_skip temp(r);
                   Value& finally_result=r.process(*finally_code);
   
         Value& body_code=params->as_junction(0, "body_code must be code");                  // no exception in try/catch or finally, writing processed body_code or catch_code
         Value& catch_code=params->as_junction(1, "catch_code must be code");                  r.write(result.processed_code);
   
         StringOrValue result;                  // write out processed finally code
         // taking snapshot of try-context                  r.write(finally_result);
         Request_context_saver try_context(r);          } else {
         try {                  // no exception in try/catch, writing processed body_code or catch_code
                 result=r.process(body_code);                  r.write(result.processed_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(pool, e);  }
   
                 Junction *junction=catch_code.get_junction();  static void _throw_operator(Request&, MethodParams& params) {
                 Value *method_frame=junction->method_frame;          if(params.count()==1 && !params[0].is_string()) {
                 Value *saved_exception_var_value=method_frame->get_element(*exception_var_name, *method_frame, false);                  if(HashStringValue *hash=params[0].get_hash()) {
                 junction->method_frame->put_element(*exception_var_name, &vhash, false);                          const char* type=0;
                 result=r.process(catch_code);                          if(Value* value=hash->get(exception_type_part_name))
                 bool handled=false;  
                 if(Value *value=static_cast<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) {  
         Pool& pool=r.pool();  
   
         if(params->size()==1) {  
                 Value& param0=params->get(0);  
                 if(Hash *hash=param0.get_hash(&method_name)) {  
                         const char *type=0;  
                         if(Value *value=static_cast<Value *>(hash->get(*exception_type_part_name)))  
                                 type=value->as_string().cstr();                                  type=value->as_string().cstr();
                         const String *source=0;                          const String* source=0;
                         if(Value *value=static_cast<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=0;                          const char* comment=0;
                         if(Value *value=                          if(Value* value=hash->get(exception_comment_part_name))
                                 static_cast<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,
                                 comment);                                  "%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 or string 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.count()>1? &params.as_string(1, "source must be string"):0;
                 const char *comment=params->size()>2?params->as_string(2, "comment must be string").cstr():0;                  const char* comment=params.count()>2? params.as_string(2, "comment must be string").cstr():0;
                 throw Exception(type, &source, "%s", comment?comment:"");                  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);
           if(seconds>0)
                   pa_sleep((int)trunc(seconds), (int)trunc((seconds-trunc(seconds))*1000000));
    }
   
   #if defined(WIN32) && defined(_DEBUG) && !defined(_WIN64)
   #       define PA_BPT
   static void _bpt(Request&, MethodParams&) {
           _asm int 3;
 }  }
           #endif
   
 // constructor  // constructor
   
 VClassMAIN::VClassMAIN(Pool& apool): VClass(apool) {  VClassMAIN::VClassMAIN(): VClass(MAIN_CLASS_NAME) {
         set_name(*NEW String(pool(), MAIN_CLASS_NAME));  
   #ifdef PA_BPT
           // ^bpt[]
           add_native_method("bpt", Method::CT_ANY, _bpt, 0, 0);
   #endif
   
         // ^if(condition){code-when-true}          // ^if(condition){code-when-true}
         // ^if(condition){code-when-true}{code-when-false}          // ^if(condition){code-when-true}{code-when-false}
         add_native_method("if", Method::CT_ANY, _if, 2, 3);          // ^if(condition){code-when-true} (another condition){code-when-true} ... {code-when-false}
           add_native_method("if", Method::CT_ANY, _if, 2, 10000, Method::CO_WITHOUT_FRAME);
   
         // ^untaint[as-is|uri|sql|js|html|html-typo]{code}          // ^untaint[as-is|uri|sql|js|html|html-typo|regex|parser-code]{code}
         add_native_method("untaint", Method::CT_ANY, _untaint, 1, 2);          add_native_method("untaint", Method::CT_ANY, _untaint, 1, 2, Method::CO_WITHOUT_FRAME);
   
         // ^taint[as-is|uri|sql|js|html|html-typo]{code}          // ^taint[as-is|uri|sql|js|html|html-typo|regex|parser-code]{code}
         add_native_method("taint", Method::CT_ANY, _taint, 1, 2);          add_native_method("taint", Method::CT_ANY, _taint, 1, 2, Method::CO_WITHOUT_FRAME);
   
         // ^process[code]          // ^apply-taint[untaint lang][string]
         add_native_method("process", Method::CT_ANY, _process, 1, 2);          add_native_method("apply-taint", Method::CT_ANY, _apply_taint, 1, 2, Method::CO_WITHOUT_FRAME);
   
           // ^process{code}
           // ^process[context]{code}[options hash]
           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, Method::CO_WITHOUT_FRAME);
   
         // ^while(condition){code}          // ^while(condition){code}
         add_native_method("while", Method::CT_ANY, _while, 2, 2);          add_native_method("while", Method::CT_ANY, _while, 2, 3, Method::CO_WITHOUT_FRAME);
   
           // ^use[file[;options hash]]
           add_native_method("use", Method::CT_ANY, _use, 1, 2);
   
         // ^use[file]          // ^break[]
         add_native_method("use", Method::CT_ANY, _use, 1, 1);          // ^break(condition)
           add_native_method("break", Method::CT_ANY, _break, 0, 1, Method::CO_WITHOUT_FRAME);
   
           // ^continue[]
           // ^continue(condition)
           add_native_method("continue", Method::CT_ANY, _continue, 0, 1, Method::CO_WITHOUT_FRAME);
   
           // ^return[]
           // ^return[result]
           add_native_method("return", Method::CT_ANY, _return, 0, 1);
   
         // ^for[i](from-number;to-number-inclusive){code}[delim]          // ^for[i](from-number;to-number-inclusive){code}[delim]
         add_native_method("for", Method::CT_ANY, _for, 3+1, 3+1+1);          add_native_method("for", Method::CT_ANY, _for, 3+1, 3+1+1);
   
         // ^eval(expr)          // ^eval(expr)
         // ^eval(expr)[format]          // ^eval(expr)[format]
         add_native_method("eval", Method::CT_ANY, _eval, 1, 2);          add_native_method("eval", Method::CT_ANY, _eval, 1, 2, Method::CO_WITHOUT_FRAME);
   
         // ^connect[protocol://user:pass@host[:port]/database]{code with ^sql-s}          // ^connect[protocol://user:pass@host[:port]/database]{code with ^sql-s}
         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          // ^cache[] get current expiration time
         add_native_method("cache", Method::CT_ANY, _cache, 1, 3);          add_native_method("cache", Method::CT_ANY, _cache, 0, 4);
                   
         // switch          // switch
   
         // ^switch[value]{cases}          // ^switch[value]{cases}
         add_native_method("switch", Method::CT_ANY, _switch, 2, 2);          add_native_method("switch", Method::CT_ANY, _switch, 2, 2, Method::CO_WITHOUT_FRAME);
   
         // ^case[value]{code}          // ^case[value]{code}
         add_native_method("case", Method::CT_ANY, _case, 2, 10000);          add_native_method("case", Method::CT_ANY, _case, 2, 10000, Method::CO_WITHOUT_FRAME);
   
         // try-catch          // try-catch
   
         // ^try{code}{catch code}          // ^try{code}{catch code}
         add_native_method("try", Method::CT_ANY, _try_operator, 2, 2);          add_native_method("try", Method::CT_ANY, _try_operator, 2, 3, Method::CO_WITHOUT_FRAME);
         // ^throw[$exception hash]          // ^throw[$exception hash]
         // ^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_classPtr VClassMAIN_create() {  VStateless_class& VClassMAIN_create() {
         #define ULN(name, LANG) \          return *new VClassMAIN;
                 untaint_lang_name2enum.put(StringPtr(new String(name)), (int)String::UL_##LANG);  
         ULN("as-is", AS_IS);  
         ULN("optimized-as-is", AS_IS|String::UL_OPTIMIZE_BIT);  
         ULN("file-spec", FILE_SPEC);  
         ULN("http-header", HTTP_HEADER);  
         ULN("mail-header", MAIL_HEADER);  
         ULN("uri", URI);  
         ULN("table", TABLE);  
         ULN("sql", SQL);  
         ULN("js", JS);  
         ULN("xml", XML);  
         ULN("optimized-xml", XML|String::UL_OPTIMIZE_BIT);  
         ULN("html", HTML);  
         ULN("optimized-html", HTML|String::UL_OPTIMIZE_BIT);  
         #undef ULN  
           
         return VStateless_classPtr(new VClassMAIN);  
 }  }

Removed from v.1.127.2.4  
changed lines
  Added in v.1.254


E-mail: