Diff for /parser3/src/classes/op.C between versions 1.124 and 1.127.2.22.2.3

version 1.124, 2002/10/31 10:23:45 version 1.127.2.22.2.3, 2003/03/19 13:16:55
Line 1 Line 1
 /** @file  /** @file
         Parser: parser @b operators.          Parser: parser @b operators.
   
         Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
 */  */
   
 static const char* IDENT_OP_C="$Date$";  static const char* 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_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 30  static const char* IDENT_OP_C="$Date$";
   
 class VClassMAIN: public VClass {  class VClassMAIN: public VClass {
 public:  public:
         VClassMAIN(Pool& apool);          VClassMAIN();
           override ~VClassMAIN() {}
 };  };
   
   // defines for statics
   
   #define SWITCH_DATA_NAME "SWITCH-DATA"
   #define CACHE_DATA_NAME "CACHE-DATA"
   #define EXCEPTION_VAR_NAME "exception"
   #define EXCEPTION_TYPE_PART_NAME "type"
   #define EXCEPTION_SOURCE_PART_NAME "source"
   #define EXCEPTION_COMMENT_PART_NAME "comment"
   #define EXCEPTION_HANDLED_PART_NAME "handled"
   
   // statics
   
   //^switch ^case
   static const String& switch_data_name(new String(SWITCH_DATA_NAME));
   //^cache
   static const String& cache_data_name(new String(CACHE_DATA_NAME));
   
   static const String& exception_var_name(new String(EXCEPTION_VAR_NAME));
   static const String& exception_type_part_name(new String(EXCEPTION_TYPE_PART_NAME));
   static const String& exception_source_part_name(new String(EXCEPTION_SOURCE_PART_NAME));
   static const String& exception_comment_part_name(new String(EXCEPTION_COMMENT_PART_NAME));
   static const String& exception_handled_part_name(new String(EXCEPTION_HANDLED_PART_NAME));
   
   // helpers
   
   class Untaint_lang_name2enum: public Hash<String* , int> {
   public:
           Untaint_lang_name2enum() {
                   #define ULN(name, LANG) \
                           put(String* (new String(name)), (value_type)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
           }
   } untaint_lang_name2enum;
   
 // methods  // methods
   
 static void _if(Request& r, const String&, MethodParams *params) {  static void _if(Request& r, const String& /*method_name*/, MethodParams* params) {
         Value& condition_code=params->as_junction(0, "condition must be expression");          ValuePtr 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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         uchar lang;          String_UL 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::UL_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,
                                 "invalid taint language");                                  "invalid taint language");
         }          }
   
         {          {
                 Value& vbody=params->as_junction(params->size()-1, "body must be code");                  ValuePtr 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, const String& /*method_name*/, MethodParams* params) {
         Pool& pool=r.pool();  
   
         uchar lang;          String_UL lang;
         if(params->size()==1)          if(params->count()==1)
                 lang=String::UL_TAINTED; // mark as simply 'tainted'. useful in table:set                  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,
                                 "invalid taint language");                                  "invalid taint language");
         }          }
   
         {          {
                 Value& vbody=params->as_no_junction(params->size()-1, "body must not be code");                  ValuePtr 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(&pool),  // process marking tainted with that lang
                         lang, true);  // force result language to specified                          lang, true);  // force result language to specified
                 r.write_pass_lang(result);                  r.write_pass_lang(result);
         }          }
 }  }
   
 static void _process(Request& r, const String& method_name, MethodParams *params) {  static void _process(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();          const String* source; // must be declared here to prevent premature destruction
         const Method *main_method;          MethodPtr main_method;
         Value& target_self=params->size()>1?          ValuePtr target_self=params->count()>1?
                 params->as_no_junction(0, "target must not be code")                  params->as_no_junction(0, "target must not be code")
                 :r.get_method_frame()->caller()->self();                  :ValuePtr(&r.get_method_frame()->caller()->self());
         {          {
                 Value& vjunction=params->as_junction(params->size()-1, "body must be code");                  ValuePtr vjunction=params->as_junction(params->count()-1, "body must be code");
   
                 VStateless_class *target_class=target_self.get_last_derived_class();                  VStateless_class *target_class=target_self->get_last_derived_class();
                 if(!target_class)                  if(!target_class)
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 &method_name,                                  method_name,
                                 "no target class");                                  "no target class");
                                   
                 // evaluate source to process                  // evaluate source to process
                 const String& source=r.process_to_string(vjunction);                  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::UL_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, MethodPtr(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, MethodPtr(0));
                                   
                 // calculate pseudo file name of processed chars                  // calculate pseudo file name of processed chars
                 // would be something like "/some/file(4) process"                  // would be something like "/some/file(4) process"
                 char local_place[MAX_STRING];                  char local_place[MAX_STRING];
 #ifndef NO_STRING_ORIGIN  #ifndef NO_STRING_ORIGIN
                 const Origin& source_origin=source.origin();                  const String_fragment::Origin& source_origin=source->origin();
                 const Origin& method_origin=method_name.origin();                  const String_fragment::Origin& method_origin=method_name->origin();
                 size_t place_size;                  size_t place_size;
                 if(source_origin.file==method_origin.file)                  if(source_origin.file==method_origin.file)
                         place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s",  // same 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,                                   source_origin.file?source_origin.file:"unknown_file", 1+source_origin.line, 
                                 method_name.cstr())+1;                                  method_name->cstr().get())+1;
                 else // different files ^process{external__file_text__or__sql}                  else // different files ^process{external__file_text__or__sql}
                         place_size=snprintf(local_place, MAX_STRING, "%s",                           place_size=snprintf(local_place, MAX_STRING, "%s", 
                                 source_origin.file?source_origin.file:"unknown_file")+1;                                  source_origin.file?source_origin.file:"unknown_file")+1;
 #else  #else
                 strncpy(local_place, method_name.cstr(), MAX_STRING-1); place[MAX_STRING-1]=0;                  strncpy(local_place, method_name->cstr(), MAX_STRING-1); place[MAX_STRING-1]=0;
                 size_t place_size=strlen(local_place)+1;                  size_t place_size=strlen(local_place)+1;
 #endif  #endif
                 char *heap_place=(char *)r.malloc(place_size);                  char *heap_place=pool.copy(local_place, place_size);
                 memcpy(heap_place, local_place, place_size);  
   
                 // process source code, append processed methods to 'self' class                  // process source code, append processed methods to 'self' class
                 // maybe-define new @main                  // maybe-define new @main
                 r.use_buf(*target_class,                  r.use_buf(*target_class,
                         source.cstr(String::UL_UNSPECIFIED, r.connection(0)),                           source->cstr(pool, String::UL_UNSPECIFIED, r.connection(0)), 
                         *new(pool) String(pool, heap_place, place_size, true /*tainted*/),                          String* (new String(heap_place, place_size, true /*tainted*/)),
                         heap_place);                          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[]
Line 159  static void _process(Request& r, const S Line 207  static void _process(Request& r, const S
         }          }
 }  }
                   
 static void _rem(Request& r, const String&, MethodParams *params) {  static void _rem(Request& r, const String& /*method_name*/, 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, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         Value& vcondition=params->as_junction(0, "condition must be expression");          ValuePtr vcondition=params->as_junction(0, "condition must be expression");
         Value& body=params->as_junction(1, "body must be code");          ValuePtr 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,                                  method_name,
                                 "endless loop detected");                                  "endless loop detected");
   
                 bool condition=r.process_to_value(vcondition,                   bool condition=r.process_to_value(vcondition, 
                                 /*0/*no name* /,*/                                  /*0/*no name* /,*/
                                 false/*don't intercept string*/).as_bool();                                  false/*don't intercept string*/)->as_bool();
                 if(!condition) // ...condition is true                  if(!condition) // ...condition is true
                         break;                          break;
   
Line 188  static void _while(Request& r, const Str Line 235  static void _while(Request& r, const Str
         }          }
 }  }
   
 static void _use(Request& r, const String& method_name, MethodParams *params) {  static void _use(Request& r, const String& method_name, MethodParams* params) {
         Value& vfile=params->as_no_junction(0, "file name must not be code");          ValuePtr 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(&pool));
 }  }
   
 static void _for(Request& r, const String& method_name, MethodParams *params) {  static void _for(Request& r, const String& method_name, 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");          ValuePtr  body_code=params->as_junction(3, "body must be code");
         Value *delim_maybe_code=params->size()>4?&params->get(4):0;          ValuePtr delim_maybe_code=params->count()>4?(*params)[4]:ValuePtr(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,                          method_name,
                         "endless loop detected");                          "endless loop detected");
   
         bool need_delim=false;          bool need_delim=false;
         VInt *vint=new(pool) VInt(pool, 0);          VIntPtr 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->size()) { // delimiter set and we have body
                         if(need_delim) // need delim & iteration produced string?                          if(need_delim) // need delim & iteration produced string?
                                 r.write_pass_lang(r.process(*delim_maybe_code));                                  r.write_pass_lang(r.process(delim_maybe_code));
                         need_delim=true;                          need_delim=true;
                 }                  }
                 r.write_pass_lang(sv_processed);                  r.write_pass_lang(sv_processed);
         }          }
 }  }
   
 static void _eval(Request& r, const String& method_name, MethodParams *params) {  static void _eval(Request& r, const String& method_name, MethodParams* params) {
         Value& expr=params->as_junction(0, "need expression");          ValuePtr expr=params->as_junction(0, "need expression");
         // evaluate expresion          // evaluate expresion
         Value *value_result=r.process_to_value(expr,           ValuePtr 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");                  ValuePtr fmt=params->as_no_junction(1, "fmt must not be code");
                   String string;
                 Pool& pool=r.pool();                  string.APPEND_CONST(
                 String& string=*new(pool) String(pool);                          format(pool, value_result->as_double(), fmt->as_string(&pool)->cstr()));
                 string.APPEND_CONST(format(pool, value_result->as_double(), fmt.as_string().cstr()));  
                 r.write_no_lang(string);                  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, const String& method_name, 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");          ValuePtr url=params->as_no_junction(0, "url must not be code");
         Value& body_code=params->as_junction(1, "body must be code");          ValuePtr 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(ValuePtr sql=r.main_class->get_element(String* (new 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(ValuePtr element=sql->get_element(String* (new String(MAIN_SQL_DRIVERS_NAME)), *sql, false)) {
                         protocol2driver_and_client=element->get_table();                          protocol2driver_and_client=element->get_table();
                 }                  }
         }          }
Line 260  struct timeval mt[2]; Line 304  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_ConnectionPtr connection=SQL_driver_manager.get_connection(pool,
                 url.as_string(), method_name, protocol2driver_and_client);                  url->as_string(&pool), method_name, protocol2driver_and_client);
   
 #ifdef RESOURCES_DEBUG  #ifdef RESOURCES_DEBUG
 //measure:after connect  //measure:after connect
Line 279  r.sql_connect_time+=t[1]-t[0]; Line 323  r.sql_connect_time+=t[1]-t[0];
                 r.write_assign_lang(r.process(body_code));                  r.write_assign_lang(r.process(body_code));
         } catch(...) { // process problem          } catch(...) { // process problem
                 connection->mark_to_rollback();                  connection->mark_to_rollback();
                 /*re*/throw;                   rethrow; 
         }          }
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Switch_data {  class Switch_data: public PA_Object {
         Request *r;  public:
         Value *searching;          Request& r;
         Value *found;          ValuePtr searching;
         Value *_default;          ValuePtr found;
           ValuePtr _default;
   public:
           Switch_data(Request& ar, ValuePtr asearching): 
             r(ar), searching(asearching) {}
 };  };
   DECLARE_OBJECT_PTR(Switch_data);
 #endif  #endif
 static void _switch(Request& r, const String&, MethodParams *params) {  static void _switch(Request& r, const String& /*method_name*/, MethodParams* params) {
         Switch_data data={&r, &r.process_to_value(params->get(0))};               Switch_dataPtr data(new Switch_data(r, r.process_to_value(params->get(0))));
         Temp_hash_value switch_data_setter(r.classes_conf, *switch_data_name, &data);          Temp_hash_value<String* , PA_Object* > 
                   switch_data_setter(r.classes_conf, switch_data_name, data);
   
         Value& cases_code=params->as_junction(1, "switch cases must be code");          ValuePtr 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(ValuePtr 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, const String& method_name, 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).get());
         if(!data)          if(!data)
                 throw Exception("parser.runtime",                  throw Exception("parser.runtime",
                         &method_name,                          method_name,
                         "without switch");                          "without switch");
   
         int count=params->size();          int count=params->count();
         Value *code=&params->as_junction(--count, "case result must be code");          ValuePtr 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));                  ValuePtr value=r.process_to_value(params->get(i));
   
                 if(value.as_string() == CASE_DEFAULT_VALUE) {                  if(*value->as_string(&pool) == 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(&pool) == *value->as_string(&pool);
                 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,                                          method_name,
                                         "duplicate found");                                          "duplicate found");
   
                         data->found=code;                          data->found=code;
Line 352  static void _case(Request& r, const Stri Line 401  static void _case(Request& r, const Stri
   
 // consts  // consts
   
 const int DATA_STRING_SERIALIZED_VERSION=0x0002;  const int DATA_STRING_SERIALIZED_VERSION=0x0004;
   
 // helper types  // helper types
   
Line 368  void cache_delete(const String& file_spe Line 417  void cache_delete(const String& file_spe
 }  }
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Cache_data {  class Cache_data: public PA_Object {
   public:
         time_t expires;          time_t expires;
 };  };
   DECLARE_OBJECT_PTR(Cache_data);
 struct Locked_process_and_cache_put_action_info {  struct Locked_process_and_cache_put_action_info {
         Request *r;          Request *r;
         Cache_data *data;          Cache_data *data;
         Value *body_code; const String *evaluated_body;          ValuePtr body_code; const String& evaluated_body;
 };  };
 #endif  #endif
   /* @todo maybe network order worth spending some effort?
           don't bothering myself with network byte order,
           am not planning to be able to move resulting file across platforms
   */
 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);
   
           =info.r->pool();
                   
         // body->process           // body->process 
         info.evaluated_body=&info.r->process_to_string(*info.body_code);          info.evaluated_body=info.r->process_to_string(info.body_code);
   
         // 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.data->expires > time(0)) {
                 // string -serialize> buffer                  // string -serialize> buffer
                 void *data; size_t data_size;                  char *data; size_t data_size;
                 info.evaluated_body->serialize(                  info.evaluated_body->serialize(pool, 
                         sizeof(Data_string_serialized_prolog),                           sizeof(Data_string_serialized_prolog), 
                         data, data_size);                          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 *>(data);
                 prolog.version=DATA_STRING_SERIALIZED_VERSION;                  prolog.version=DATA_STRING_SERIALIZED_VERSION;
                 prolog.expires=info.data->expires;                  prolog.expires=info.data->expires;
                                   
Line 401  static void locked_process_and_cache_put Line 458  static void locked_process_and_cache_put
         } else // expired!          } else // expired!
                 info.data->expires=0; // flag it so that could be easily checked by caller                  info.data->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,                                                                          ValuePtr body_code,
                                                                         Cache_data& data,                                                                          Cache_data& data,
                                                                         const String& file_spec) {                                                                          const String& file_spec) {
         Locked_process_and_cache_put_action_info info={          Locked_process_and_cache_put_action_info info;
                 &r,          info.r=&r;
                 &data,          info.data=&data;
                 &body_code          info.body_code=body_code;
         };  
   
         const String *result=file_write_action_under_lock(          const String& result=file_write_action_under_lock(
                 file_spec,                   file_spec, 
                 "cache_put", locked_process_and_cache_put_action, &info,                  "cache_put", locked_process_and_cache_put_action, &info,
                 false/*as_text*/,                  false/*as_text*/,
Line 423  const String *locked_process_and_cache_p Line 479  const String *locked_process_and_cache_p
                 cache_delete(file_spec);                  cache_delete(file_spec);
         return result;          return result;
 }  }
 String *cache_get(Pool& pool, const String& file_spec, time_t now) {  const String& cache_getCharset& charset, const String& file_spec, time_t now) {
         void* data; size_t data_size;          File_read_result file=file_read(pool, charset, file_spec, 
         if(file_read(pool, file_spec,   
                            data, data_size,   
                            false/*as_text*/,                              false/*as_text*/, 
                            false/*fail_on_read_problem*/)                             0, //no params
             && data_size/* ignore reads which are empty due to                              false/*fail_on_read_problem*/);
           if(file.success && file.size/* 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.data);
   
                 String *result=new(pool) String(pool);                  const String& result(new String);
                 if(                  if(
                         data_size>=sizeof(Data_string_serialized_prolog)                          file.size>=sizeof(Data_string_serialized_prolog)
                         && prolog.version==DATA_STRING_SERIALIZED_VERSION                          && prolog.version==DATA_STRING_SERIALIZED_VERSION
                         && prolog.expires > now                          && prolog.expires > now
                         && result->deserialize(                          && result->deserialize(
                                 sizeof(Data_string_serialized_prolog),  data, data_size, file_spec.cstr()))                                  sizeof(Data_string_serialized_prolog),  file.data, file.size, file_spec->cstr()))
                         return result;                          return result;
         }          }
   
         return 0;          return 0;
 }  }
 static time_t as_expires(Request& r, const String& method_name, MethodParams *params,   static time_t as_expires(Request& r, const String& method_name, 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, const String& method_name, 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_data* data=static_cast<Cache_data*>(r.classes_conf.get(cache_data_name).get());
                 if(!data)                  if(!data)
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 &method_name,                                  method_name,
                                 "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, method_name, params, 0, now);
Line 487  static void _cache(Request& r, const Str Line 540  static void _cache(Request& r, const Str
         }          }
                   
         // 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_dataPtr data(new Cache_data);
         Temp_hash_value cache_data_setter(r.classes_conf, *cache_data_name, &data);          Temp_hash_value<String* , PA_Object* > 
         data.expires=as_expires(r, method_name, params, 1, now);                  cache_data_setter(r.classes_conf, cache_data_name, data);
         Value& body_code=params->as_junction(2, "body must be code");          data->expires=as_expires(r, method_name, params, 1, now);
           ValuePtr body_code=params->as_junction(2, "body must be code");
   
         if(data.expires>now) { // valid 'expires' specified? try cached copy...          if(data->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 512  static void _cache(Request& r, const Str Line 566  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?                          if(const String& cached_body=cache_get(pool, r.charsets.source(), file_spec, now)) { // have cached copy?
                                 // write it out                                   // write it out 
                                 r.write_assign_lang(*cached_body);                                  r.write_assign_lang(*cached_body);
                                 // happy with it                                  // happy with it
Line 520  static void _cache(Request& r, const Str Line 574  static void _cache(Request& r, const Str
                         }                          }
   
                         // 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, *data, 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
Line 532  static void _cache(Request& r, const Str Line 586  static void _cache(Request& r, const Str
                         }                          }
                 }                  }
                 throw Exception(0,                  throw Exception(0,
                         &file_spec,                          file_spec,
                         "locking problem");                          "locking problem");
         } else {           } else { 
                 // instructed not to cache; forget cached copy                  // instructed not to cache; forget cached copy
Line 540  static void _cache(Request& r, const Str Line 594  static void _cache(Request& r, const Str
                 // process                  // process
                 const String& processed_body=r.process_to_string(body_code);                  const String& processed_body=r.process_to_string(body_code);
                 // write it out                   // write it out 
                 r.write_assign_lang(processed_body);                  r.write_assign_lang(*processed_body);
                 // happy with it                  // happy with it
                 return;                  return;
         }          }
Line 548  static void _cache(Request& r, const Str Line 602  static void _cache(Request& r, const Str
 }  }
   
 // also used in pa_request.C to pass param to @unhandled_exception  // also used in pa_request.C to pass param to @unhandled_exception
 VHash& exception2vhash(Pool& pool, const Exception& e) {  VHashPtr exception2vhashconst Exception& e) {
         VHash& result=*new(pool) VHash(pool);          VHashPtr result(new VHash);
         Hash& hash=result.hash(0);          HashStringValue& hash=result->hash(Exception::undefined_source);
         if(const char *type=e.type(true))          if(const char* type=e.type(true))
                 hash.put(*exception_type_part_name, new(pool) VString(*new(pool) String(pool, type)));                  hash.put(exception_type_part_name, ValuePtr(new VString(String* (new String(type)))));
         if(const String *asource=e.problem_source()) {          if(const String& asource=e.problem_source()) {
                 String& source=*new(pool) String(pool);                   const String* source(new String); 
                 source.append(*asource, String::UL_TAINTED, true/*forced*/);                  source->append(*asource, String::UL_TAINTED, true/*forced*/);
   
                 hash.put(*exception_source_part_name, new(pool) VString(source));                  hash.put(exception_source_part_name, ValuePtr(new VString(source)));
 #ifndef NO_STRING_ORIGIN  #ifndef NO_STRING_ORIGIN
                 const Origin& origin=source.origin();                  const String_fragment::Origin& origin=source->origin();
                 hash.put(*new(pool) String(pool, "file", 0, true),                  hash.put(String* (new String("file", 0, true)),
                         new(pool) VString(*new(pool) String(pool, origin.file)));                          ValuePtr(new VString(String* (new String(origin.file)))));
                 hash.put(*new(pool) String(pool, "lineno"),                  hash.put(String* (new String("lineno")),
                         new(pool) VInt(pool, 1+origin.line));                          ValuePtr(new VInt(1+origin.line)));
 #endif  #endif
         }          }
         if(const char *ecomment=e.comment(true)) {          if(const char* ecomment=e.comment(true)) {
                 int comment_size=strlen(ecomment);                  char *pcomment=pool.copy(ecomment);
                 char *pcomment=(char *)pool.malloc(comment_size);                  hash.put(exception_comment_part_name, 
                 memcpy(pcomment, ecomment, comment_size);                          ValuePtr(new VString(String* (new String(pcomment, 0, true/*tainted*/)))));
                 hash.put(*exception_comment_part_name,   
                         new(pool) VString(*new(pool) String(pool, pcomment, comment_size, true/*tainted*/)));  
         }          }
         hash.put(*exception_handled_part_name,           hash.put(exception_handled_part_name, 
                 new(pool) VBool(pool, false));                  ValuePtr(new VBool(false)));
   
         return result;          return result;
 }  }
   
 static void _try_operator(Request& r, const String& method_name, MethodParams *params) {  static void _try_operator(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         Value& body_code=params->as_junction(0, "body_code must be code");          ValuePtr body_code=params->as_junction(0, "body_code must be code");
         Value& catch_code=params->as_junction(1, "catch_code must be code");          ValuePtr catch_code=params->as_junction(1, "catch_code must be code");
   
         StringOrValue result;          StringOrValue result;
         // taking snapshot of try-context          // taking snapshot of try-context
Line 594  static void _try_operator(Request& r, co Line 645  static void _try_operator(Request& r, co
                 Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error]                  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                  try_context.restore(); // restoring try-context to perform catch-code
   
                 VHash& vhash=exception2vhash(pool, e);                  VHashPtr vhash=exception2vhash(pool, e);
   
                 Junction *junction=catch_code.get_junction();                  JunctionPtr junction=catch_code->get_junction();
                 Value *method_frame=junction->method_frame;                  Value* method_frame=junction->method_frame;
                 Value *saved_exception_var_value=method_frame->get_element(*exception_var_name, method_frame, false);                  ValuePtr saved_exception_var_value=method_frame->get_element(exception_var_name, *method_frame, false);
                 junction->method_frame->put_element(*exception_var_name, &vhash, false);                  junction->method_frame->put_element(exception_var_name, vhash, false);
                 result=r.process(catch_code);                  result=r.process(catch_code);
                 bool handled=false;                  bool handled=false;
                 if(Value *value=static_cast<Value *>(vhash.hash(0).get(*exception_handled_part_name)))                  if(ValuePtr value=
                           vhash->hash(Exception::undefined_source).get(exception_handled_part_name))
                         handled=value->as_bool();                                         handled=value->as_bool();               
                 junction->method_frame->put_element(*exception_var_name, saved_exception_var_value, false);                  junction->method_frame->put_element(exception_var_name, saved_exception_var_value, false);
   
                 if(!handled) {                  if(!handled) {
                         throw_context.restore(); // restoring throw-context [exception were not handled]                          throw_context.restore(); // restoring throw-context [exception were not handled]
Line 615  static void _try_operator(Request& r, co Line 667  static void _try_operator(Request& r, co
         r.write_pass_lang(result);          r.write_pass_lang(result);
 }  }
   
 static void _throw_operator(Request& r, const String& method_name, MethodParams *params) {  static void _throw_operator(Request& r, const String& method_name, MethodParams* params) {
         Pool& pool=r.pool();  
   
         if(params->size()==1) {          if(params->count()==1) {
                 Value& param0=params->get(0);                  ValuePtr param0=(*params)[0];
                 if(Hash *hash=param0.get_hash(&method_name)) {                  if(HashStringValue *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(ValuePtr value=hash->get(exception_type_part_name))
                                 type=value->as_string().cstr();                                  type=value->as_string(&pool)->cstr();
                         const String *source=0;                          const String* source;
                         if(Value *value=static_cast<Value *>(hash->get(*exception_source_part_name)))                          if(ValuePtr value=hash->get(exception_source_part_name))
                                 source=&value->as_string();                                  source=value->as_string(&pool);
                         const char *comment=0;                          const char* comment;
                         if(Value *value=                          if(ValuePtr value=hash->get(exception_comment_part_name))
                                 static_cast<Value *>(hash->get(*exception_comment_part_name)))                                  comment=value->as_string(&pool)->cstr();
                                 comment=value->as_string().cstr();  
   
                         throw Exception(type,                          throw Exception(type,
                                 source?source:&method_name,                                  source?source:method_name,
                                 comment);                                  "%s", comment?comment.get():"");
                 } else                  } else
                         throw Exception("parser.runtime",                          throw Exception("parser.runtime",
                                 &method_name,                                  method_name,
                                 "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()
                 throw Exception(type, &source, "%s", comment?comment:"");                          :CharPtr(0);
                   throw Exception(type, source, "%s", comment?comment.get():"");
         }          }
 }  }
           
   #if defined(WIN32) && defined(_DEBUG)
   #       define PA_BPT
   static void _bpt(Request& r, const String& method_name, 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(String* (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 709  VClassMAIN::VClassMAIN(Pool& apool): VCl Line 772  VClassMAIN::VClassMAIN(Pool& apool): VCl
   
 // constructor & configurator  // constructor & configurator
   
 VClass& VClassMAIN_create(Pool& pool) {  VStateless_classPtr VClassMAIN_create() {
         return *new(pool) VClassMAIN(pool);          
           return VStateless_classPtr(new VClassMAIN);
 }  }

Removed from v.1.124  
changed lines
  Added in v.1.127.2.22.2.3


E-mail: