Diff for /parser3/src/classes/op.C between versions 1.127.2.5 and 1.136

version 1.127.2.5, 2003/01/31 12:34:27 version 1.136, 2003/11/19 11:00:32
Line 8 Line 8
 static const char* IDENT_OP_C="$Date$";  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"
   
 // limits  // limits
   
Line 27  static const char* IDENT_OP_C="$Date$"; Line 31  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 Hash<const String::Body, String::Language> {
   public:
           Untaint_lang_name2enum() {
                   #define ULN(name, LANG) \
                           put(String::Body(name), (value_type)(String::L_##LANG));
                   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("table", TABLE);
                   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);
                   #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");          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* /,*/                  /*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->size()>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) {
         Pool& pool=r.pool();  
   
         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 '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_int(lang_name);                  lang=untaint_lang_name2enum.get(lang_name);
                 if(!lang)                  if(!lang)
                         throw Exception(0,                          throw Exception(0,
                                 &lang_name,                                  &lang_name,
Line 64  static void _untaint(Request& r, const S Line 106  static void _untaint(Request& r, const S
         }          }
   
         {          {
                 Value& vbody=params->as_junction(params->size()-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&, MethodParams *params) {  static void _taint(Request& r, MethodParams& params) {
         Pool& pool=r.pool();          String::Language lang;
           if(params.count()==1)
         uchar lang;                  lang=String::L_TAINTED; // mark as simply 'tainted'. useful in table:set
         if(params->size()==1)  
                 lang=String::UL_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_int(lang_name);                  lang=untaint_lang_name2enum.get(lang_name);
                 if(!lang)                  if(!lang)
                         throw Exception(0,                          throw Exception(0,
                                 &lang_name,                                  &lang_name,
Line 87  static void _taint(Request& r, const Str Line 127  static void _taint(Request& r, const Str
         }          }
   
         {          {
                 Value& vbody=params->as_no_junction(params->size()-1, "body must not be code");                  Value& vbody=params.as_no_junction(params.count()-1, "body must not be code");
                                   
                 String& result=*new(pool) String(pool);                  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
Line 97  static void _taint(Request& r, const Str Line 137  static void _taint(Request& r, const Str
         }          }
 }  }
   
 static void _process(Request& r, const String& method_name, MethodParams *params) {  static void _process(Request& r, MethodParams& params) {
         Pool& pool=r.pool();          Method* main_method;
         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();          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();
                 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  
                 const String& source=r.process_to_string(vjunction);  
   
                 // temporary remove language change                  // temporary remove language change
                 Temp_lang temp_lang(r, String::UL_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, r.main_method_name, 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, 0);                  Temp_method temp_method_auto(*target_class, auto_method_name, 0);
                   
                 // calculate pseudo file name of processed chars                  size_t main_alias_index=index+1;
                 // would be something like "/some/file(4) process"                  const String* main_alias=0;
                 char local_place[MAX_STRING];                  if(main_alias_index<params.count())
 #ifndef NO_STRING_ORIGIN                          main_alias=&params.as_string(main_alias_index, "main alias must be string");
                 const Origin& source_origin=source.origin();  
                 const Origin& method_origin=method_name.origin();                  if(const String* file_name=params[index].get_string()) { // ^process...[file]
                 size_t place_size;                          r.use_file(*target_class, r.absolute(*file_name), main_alias, true/*ignore_class_path*/);
                 if(source_origin.file==method_origin.file)                  } else { // process...{string}
                         place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s",  // same file                          Value& vjunction=params.as_junction(index, "body must be code");
                                 source_origin.file?source_origin.file:"unknown_file", 1+source_origin.line,                           // evaluate source to process
                                 method_name.cstr())+1;                          const String& source=r.process_to_string(vjunction);
                 else // different files ^process{external__file_text__or__sql}                          r.use_buf(*target_class,
                         place_size=snprintf(local_place, MAX_STRING, "%s",                                   source.cstr(String::L_UNSPECIFIED, r.connection(false)),
                                 source_origin.file?source_origin.file:"unknown_file")+1;                                  main_alias,
 #else                                  pseudo_file_no__process);               
                 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                  // 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&, MethodParams *params) {  static void _rem(Request& r, 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();          Value& vcondition=params.as_junction(0, "condition must be expression");
           Value& body=params.as_junction(1, "body must be code");
         Value& vcondition=params->as_junction(0, "condition must be expression");  
         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, 
Line 192  static void _while(Request& r, const Str Line 220  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) {
         Pool& pool=r.pool();          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->size()>4?&params->get(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;
         VInt *vint=new(pool) VInt(pool, 0);          VInt* vint=new VInt(0);
         r.get_method_frame()->caller()->put_element(var_name, vint, false);          r.get_method_frame()->caller()->put_element(var_name, vint, false);
         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->size()) { // 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;
Line 227  static void _for(Request& r, const Strin Line 254  static void _for(Request& r, const Strin
         }          }
 }  }
   
 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* /,*/                  /*0/*no name YET* /,*/
                 true/*don't intercept string*/).as_expr_result();                  true/*don't intercept string*/).as_expr_result();
         if(params->size()>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())));
                 Pool& pool=r.pool();  
                 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_no_lang(value_result);
 }  }
   
 static void _connect(Request& r, const String& method_name, MethodParams *params) {  static void _connect(Request& r, MethodParams& params) {
         Pool& pool=r.pool();  
 #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(*new(pool) String(pool, 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(*new(pool) String(pool, 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 264  struct timeval mt[2]; Line 286  struct timeval mt[2];
 gettimeofday(&mt[0],NULL);  gettimeofday(&mt[0],NULL);
 #endif  #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);
   
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 //measure:after connect  //measure:after connect
Line 277  for(int i=0;i<2;i++) Line 299  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();
                 /*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;          Value& searching;
         Value *_default;          Value* found;
           Value* _default;
   public:
           Switch_data(Request& ar, Value& asearching): 
             r(ar), searching(asearching) {}
 };  };
 #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_to_value(params[0]));
         Temp_hash_value switch_data_setter(r.classes_conf, *switch_data_name, &data);          Temp_hash_value<const String::Body, 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, 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<<}                  // setting code context, would execute in ^switch[...]{>>context<<}
                 //selected_code->get_junction()->change_context(cases_code.get_junction());                  //selected_code->get_junction()->change_context(cases_code.get_junction());
                 r.write_pass_lang(r.process(*selected_code));                  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();          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<<}          // killing context for safety, would execute in ^switch[...]{>>context<<}
         // reason: context is stacked, and it would become invalid afterwards          // reason: context is stacked, and it would become invalid afterwards
         //code->get_junction()->change_context(0);          //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);
                   junction->method_frame->put_element(exception_var_name, &details.vhash, false);
                   result.processed_code=r.process(*catch_code);
                   
                   // retriving $exception.handled, restoring $exception var
                   Value* vhandled=details.vhash.hash().get(exception_handled_part_name);
                   junction->method_frame->put_element(exception_var_name, saved_exception_var_value, false);
   
                   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=0x0003;  const int DATA_STRING_SERIALIZED_VERSION=0x0005;
   
 // helper types  // helper types
   
Line 372  void cache_delete(const String& file_spe Line 457  void cache_delete(const String& file_spe
 }  }
   
 #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 StringOrValue process_cache_body_code(Request& r, Value* body_code) {
           return StringOrValue(&r.process_to_string(*body_code), 0);
   }
   
 /* @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 483  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);
           
         // 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
                 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};
                 &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;
 }  }
 String *cache_get(Pool& pool, const String& file_spec, time_t now) {  struct Cache_get_result {
         void* data; size_t data_size;          const String* body;
         if(file_read(pool, file_spec,           bool expired;
                            data, data_size,   } cache_get(Request_charsets& charsets, const String& file_spec, time_t now) {
           Cache_get_result result={0};
   
           File_read_result file=file_read(charsets, file_spec, 
                            false/*as_text*/,                              false/*as_text*/, 
                            0, 0, //no params&out_fields                             0, //no params
                            false/*fail_on_read_problem*/)                             false/*fail_on_read_problem*/);
             && data_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=
                         *static_cast<Data_string_serialized_prolog *>(data);                          *reinterpret_cast<Data_string_serialized_prolog *>(file.str);
   
                 String *result=new(pool) String(pool);                  String* body=new String;
                 if(                  if(
                         data_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),  data, data_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;
         Value& vlifespan_or_expires=params->get(index);          if(Value* vdate=params[index].as(VDATE_TYPE, false))
         if(Value *vdate=vlifespan_or_expires.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) {
         Pool& pool=r.pool();  
         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->size()==1) {          if(params.count()==1) {
                 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;
         }          }
                   
         // 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_data data;          Cache_scope scope={as_expires(r, params, 1, now)};
         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...          Temp_hash_value<const String::Body, void*> 
                   cache_scope_setter(r.classes_conf, cache_data_name, &scope);
           Value& body_code=params.as_junction(2, "body_code must be code");
           Value* catch_code=0;
           if(params.count()>3)
                   catch_code=&params.as_junction(3, "catch_code must be code");
   
           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 521  static void _cache(Request& r, const Str Line 636  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(String *cached_body=cache_get(pool, 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,
Line 556  static void _cache(Request& r, const Str Line 677  static void _cache(Request& r, const Str
         // 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& exception2vhash(Pool& pool, const Exception& e) {          return r.process(*body_code);
         VHash& result=*new(pool) VHash(pool);  
         Hash& hash=result.hash(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;  
 }  }
   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
         Pool& pool=r.pool();          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(pool, 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=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) {  static void _throw_operator(Request& r, MethodParams& params) {
         Pool& pool=r.pool();          if(params.count()==1) {
                   if(HashStringValue *hash=params[0].get_hash()) {
         if(params->size()==1) {  
                 Value& param0=params->get(0);  
                 if(Hash *hash=param0.get_hash(&method_name)) {  
                         const char* type=0;                          const char* type=0;
                         if(Value *value=static_cast<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=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;
                         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 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->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:"");
         }          }
    }
   
   #if defined(WIN32) && defined(_DEBUG)
   #       define PA_BPT
   static void _bpt(Request& r, MethodParams& params) {
           _asm int 3;
 }  }
           #endif
   
 // constructor  // constructor
   
 VClassMAIN::VClassMAIN(Pool& apool): VClass(apool) {  VClassMAIN::VClassMAIN(): VClass() {
         set_name(*NEW String(pool(), MAIN_CLASS_NAME));          set_name(*new String(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}
Line 694  VClassMAIN::VClassMAIN(Pool& apool): VCl Line 776  VClassMAIN::VClassMAIN(Pool& apool): VCl
         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 718  VClassMAIN::VClassMAIN(Pool& apool): VCl Line 800  VClassMAIN::VClassMAIN(Pool& apool): VCl
   
 // 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.5  
changed lines
  Added in v.1.136


E-mail: