|
|
| version 1.89, 2002/04/16 08:22:19 | version 1.128, 2003/04/04 09:59:56 |
|---|---|
| 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) |
| $Id$ | |
| */ | */ |
| static const char* IDENT_OP_C="$Date$"; | |
| #include "classes.h" | #include "classes.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" | |
| // limits | // limits |
| Line 20 | Line 21 |
| // defines | // defines |
| #define OP_CLASS_NAME "OP" | |
| #define CASE_DEFAULT_VALUE "DEFAULT" | #define CASE_DEFAULT_VALUE "DEFAULT" |
| // class | // class |
| class MOP : public Methoded { | class VClassMAIN: public VClass { |
| public: | public: |
| MOP(Pool& pool); | VClassMAIN(Pool& apool); |
| public: // Methoded | |
| bool used_directly() { return true; } | |
| void configure_user(Request& r); | |
| private: | |
| String main_sql_name; | |
| String main_sql_drivers_name; | |
| }; | }; |
| // methods | // methods |
| Line 92 static void _taint(Request& r, const Str | Line 85 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->size()-1, "body must not be code"); |
| String result(r.pool()); | String& result=*new(pool) String(pool); |
| 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 101 static void _taint(Request& r, const Str | Line 94 static void _taint(Request& r, const Str |
| } | } |
| static void _process(Request& r, const String& method_name, MethodParams *params) { | static void _process(Request& r, const String& method_name, MethodParams *params) { |
| // calculate pseudo file name of processed chars | Pool& pool=r.pool(); |
| // would be something like "/some/file(4) process" | |
| char local_place[MAX_STRING]; | |
| #ifndef NO_STRING_ORIGIN | |
| const Origin& origin=method_name.origin(); | |
| size_t place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s", | |
| origin.file?origin.file:"unknown_file", 1+origin.line, | |
| method_name.cstr())+1; | |
| #else | |
| strncpy(local_place, method_name.cstr(), MAX_STRING-1); place[MAX_STRING-1]=0; | |
| size_t place_size=strlen(local_place)+1; | |
| #endif | |
| char *heap_place=(char *)r.malloc(place_size); | |
| memcpy(heap_place, local_place, place_size); | |
| VStateless_class& self_class=*r.self->get_class(); | |
| const 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(); | |
| if(!target_class) | |
| throw Exception("parser.runtime", | |
| &method_name, | |
| "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::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(self_class, *main_method_name, 0); | Temp_method temp_method_main(*target_class, r.main_method_name, 0); |
| // temporary zero @auto so it wouldn't be auto-called in Request::use_buf | // temporary zero @auto so it wouldn't be auto-called in Request::use_buf |
| Temp_method temp_method_auto(self_class, *auto_method_name, 0); | Temp_method temp_method_auto(*target_class, *auto_method_name, 0); |
| // evaluate source to process | // calculate pseudo file name of processed chars |
| const String& source=r.process_to_string(params->as_junction(0, "body must be code")); | // would be something like "/some/file(4) process" |
| char local_place[MAX_STRING]; | |
| #ifndef NO_STRING_ORIGIN | |
| const Origin& source_origin=source.origin(); | |
| const Origin& method_origin=method_name.origin(); | |
| size_t place_size; | |
| if(source_origin.file==method_origin.file) | |
| place_size=snprintf(local_place, MAX_STRING, "%s(%d) %s", // same file | |
| source_origin.file?source_origin.file:"unknown_file", 1+source_origin.line, | |
| method_name.cstr())+1; | |
| else // different files ^process{external__file_text__or__sql} | |
| place_size=snprintf(local_place, MAX_STRING, "%s", | |
| source_origin.file?source_origin.file:"unknown_file")+1; | |
| #else | |
| strncpy(local_place, method_name.cstr(), MAX_STRING-1); place[MAX_STRING-1]=0; | |
| size_t place_size=strlen(local_place)+1; | |
| #endif | |
| char *heap_place=(char *)r.malloc(place_size); | |
| memcpy(heap_place, local_place, place_size); | |
| // process source code, append processed methods to 'self' class | // process source code, append processed methods to 'self' class |
| // maybe-define new @main | // maybe-define new @main |
| r.use_buf( | r.use_buf(*target_class, |
| source.cstr(String::UL_UNSPECIFIED, r.connection(0)), | source.cstr(String::UL_UNSPECIFIED, r.connection(0)), |
| heap_place, | *new(pool) String(pool, heap_place, place_size, true /*tainted*/), |
| &self_class); | heap_place); |
| // main_method | // main_method |
| main_method=self_class.get_method(*main_method_name); | main_method=target_class->get_method(r.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 | |
| 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); |
| } | } |
| Line 179 static void _while(Request& r, const Str | Line 190 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"); | Value& vfile=params->as_no_junction(0, "file name must not be code"); |
| r.use_file(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, const String& method_name, MethodParams *params) { |
| Line 197 static void _for(Request& r, const Strin | Line 208 static void _for(Request& r, const Strin |
| bool need_delim=false; | bool need_delim=false; |
| VInt *vint=new(pool) VInt(pool, 0); | VInt *vint=new(pool) VInt(pool, 0); |
| 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); |
| r.root->put_element(var_name, vint); | |
| StringOrValue processed_body=r.process(body_code); | StringOrValue sv_processed=r.process(body_code); |
| if(delim_maybe_code) { // delimiter set? | const String *s_processed=sv_processed.get_string(); |
| const String *string=processed_body.get_string(); | if(delim_maybe_code && s_processed && s_processed->size()) { // delimiter set and we have body |
| if(need_delim && string && string->size()) // 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(processed_body); | 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"); | Value& expr=params->as_junction(0, "need expression"); |
| // evaluate expresion | // evaluate expresion |
| 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->size()>1) { |
| Line 223 static void _eval(Request& r, const Stri | Line 234 static void _eval(Request& r, const Stri |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| String& string=*new(pool) String(pool); | String& string=*new(pool) String(pool); |
| string.APPEND_CONST(format(pool, result->as_double(), fmt.as_string().cstr())); | string.APPEND_CONST(format(pool, value_result->as_double(), fmt.as_string().cstr())); |
| result=new(pool) VString(string); | r.write_no_lang(string); |
| } | } else |
| result->set_name(method_name); | r.write_no_lang(*value_result); |
| r.write_no_lang(*result); | |
| } | } |
| static void _connect(Request& r, const String& method_name, MethodParams *params) { | static void _connect(Request& r, const String& method_name, MethodParams *params) { |
| Line 238 struct timeval mt[2]; | Line 248 struct timeval mt[2]; |
| 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= | Table *protocol2driver_and_client=0; |
| static_cast<Table *>(r.classes_conf.get(r.OP.name())); | if(Value *sql=r.main_class.get_element(*new(pool) String(pool, MAIN_SQL_NAME), r.main_class, false)) { |
| if(Value *element=sql->get_element(*new(pool) String(pool, MAIN_SQL_DRIVERS_NAME), *sql, false)) { | |
| protocol2driver_and_client=element->get_table(); | |
| } | |
| } | |
| #ifdef RESOURCES_DEBUG | #ifdef RESOURCES_DEBUG |
| //measure:before | //measure:before |
| Line 287 static void _switch(Request& r, const St | Line 301 static void _switch(Request& r, const St |
| 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)); |
| } | } |
| } | } |
| Line 306 static void _case(Request& r, const Stri | Line 320 static void _case(Request& r, const Stri |
| // 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->get(i)); |
| Line 388 static void locked_process_and_cache_put | Line 402 static void locked_process_and_cache_put |
| 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, | Value& 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, | &r, |
| &data, | &data, |
| Line 402 const String *locked_process_and_cache_p | Line 416 const String *locked_process_and_cache_p |
| "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*/) ? info.evaluated_body: 0; | false/*block*/, |
| if(data.expires==0) | false/*fail on lock problem*/) ? info.evaluated_body: 0; |
| time_t now=time(0); | |
| if(data.expires<=now) | |
| cache_delete(file_spec); | cache_delete(file_spec); |
| return result; | return result; |
| } | } |
| Line 412 String *cache_get(Pool& pool, const Stri | Line 428 String *cache_get(Pool& pool, const Stri |
| if(file_read(pool, file_spec, | if(file_read(pool, file_spec, |
| data, data_size, | data, data_size, |
| false/*as_text*/, | false/*as_text*/, |
| 0, 0, //no params&out_fields | |
| false/*fail_on_read_problem*/) | false/*fail_on_read_problem*/) |
| && data_size/* ignore reads which are empty due to | && data_size/* ignore reads which are empty due to |
| non-unary open+lockEX conflict with lockSH */) { | non-unary open+lockEX conflict with lockSH */) { |
| Line 435 static time_t as_expires(Request& r, con | Line 452 static time_t as_expires(Request& r, con |
| int index, time_t now) { | int index, time_t now) { |
| time_t result; | time_t result; |
| Value& vlifespan_or_expires=params->get(index); | Value& vlifespan_or_expires=params->get(index); |
| if(strcmp(vlifespan_or_expires.type(), VDATE_TYPE)==0) | if(Value *vdate=vlifespan_or_expires.as(VDATE_TYPE, false)) |
| result=static_cast<VDate&>(vlifespan_or_expires).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); |
| Line 535 static void _cache(Request& r, const Str | Line 552 static void _cache(Request& r, const Str |
| VHash& exception2vhash(Pool& pool, const Exception& e) { | VHash& exception2vhash(Pool& pool, const Exception& e) { |
| VHash& result=*new(pool) VHash(pool); | VHash& result=*new(pool) VHash(pool); |
| Hash& hash=result.hash(0); | Hash& hash=result.hash(0); |
| if(const char *type=e.type()) | 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, new(pool) VString(*new(pool) String(pool, type))); |
| if(const String *asource=e.problem_source()) { | if(const String *asource=e.problem_source()) { |
| String& source=*new(pool) String(pool); | String& source=*new(pool) String(pool); |
| source.append(*asource, String::UL_TAINTED, true/*forced*/); | source.append(*asource, String::UL_TAINTED, true/*forced*/); |
| result.set_name(source); | |
| hash.put(*exception_source_part_name, new(pool) VString(source)); | hash.put(*exception_source_part_name, new(pool) VString(source)); |
| #ifndef NO_STRING_ORIGIN | #ifndef NO_STRING_ORIGIN |
| Line 551 VHash& exception2vhash(Pool& pool, const | Line 567 VHash& exception2vhash(Pool& pool, const |
| new(pool) VInt(pool, 1+origin.line)); | new(pool) VInt(pool, 1+origin.line)); |
| #endif | #endif |
| } | } |
| if(const char *ecomment=e.comment()) { | if(const char *ecomment=e.comment(true)) { |
| int comment_size=strlen(ecomment); | int comment_size=strlen(ecomment); |
| char *pcomment=(char *)pool.malloc(comment_size); | char *pcomment=(char *)pool.malloc(comment_size); |
| memcpy(pcomment, ecomment, comment_size); | memcpy(pcomment, ecomment, comment_size); |
| Line 571 static void _try_operator(Request& r, co | Line 587 static void _try_operator(Request& r, co |
| Value& catch_code=params->as_junction(1, "catch_code must be code"); | Value& catch_code=params->as_junction(1, "catch_code must be code"); |
| StringOrValue result; | StringOrValue result; |
| // taking snapshot of try-context | |
| // taking snapshot of request processing status | Request_context_saver try_context(r); |
| //int ssexception_trace=r.exception_trace.top_index(); | |
| int sstack=r.stack.top_index(); | |
| Value *sself=r.self, *sroot=r.root, *srcontext=r.rcontext; | |
| WContext *swcontext=r.wcontext; | |
| try { | try { |
| result=r.process(body_code); | result=r.process(body_code); |
| } catch(const Exception& e) { | } catch(const Exception& e) { |
| // restoring request processing status | Request_context_saver throw_context(r); // taking snapshot of throw-context [stack trace contains error] |
| //r.exception_trace.top_index(ssexception_trace); | try_context.restore(); // restoring try-context to perform catch-code |
| r.stack.top_index(sstack); | |
| r.self=sself; r.root=sroot, r.rcontext=srcontext; r.wcontext=swcontext; | |
| VHash& vhash=exception2vhash(pool, e); | VHash& vhash=exception2vhash(pool, e); |
| Junction *junction=catch_code.get_junction(); | Junction *junction=catch_code.get_junction(); |
| Value *saved_exception_var_value=junction->root->get_element(*exception_var_name); | Value *method_frame=junction->method_frame; |
| junction->root->put_element(*exception_var_name, &vhash); | 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); | 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(Value *value=static_cast<Value *>(vhash.hash(0).get(*exception_handled_part_name))) |
| handled=value->as_bool(); | handled=value->as_bool(); |
| junction->root->put_element(*exception_var_name, saved_exception_var_value); | 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(e); // rethrow | throw(e); // rethrow |
| } | |
| } | } |
| // write it out | // write out result |
| r.write_pass_lang(result); | r.write_pass_lang(result); |
| } | } |
| Line 631 static void _throw_operator(Request& r, | Line 643 static void _throw_operator(Request& r, |
| } 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->as_string(2, "comment must be string").cstr(); | const char *comment=params->size()>2?params->as_string(2, "comment must be string").cstr():0; |
| throw Exception(type, &source, comment); | throw Exception(type, &source, "%s", comment?comment:""); |
| } | } |
| } | } |
| // constructor | // constructor |
| MOP::MOP(Pool& apool) : Methoded(apool), | VClassMAIN::VClassMAIN(Pool& apool): VClass(apool) { |
| main_sql_name(apool, MAIN_SQL_NAME), | set_name(*NEW String(pool(), MAIN_CLASS_NAME)); |
| main_sql_drivers_name(apool, MAIN_SQL_DRIVERS_NAME) | |
| { | |
| set_name(*NEW String(pool(), OP_CLASS_NAME)); | |
| // ^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 655 MOP::MOP(Pool& apool) : Methoded(apool), | Line 664 MOP::MOP(Pool& apool) : Methoded(apool), |
| add_native_method("taint", Method::CT_ANY, _taint, 1, 2); | add_native_method("taint", Method::CT_ANY, _taint, 1, 2); |
| // ^process[code] | // ^process[code] |
| add_native_method("process", Method::CT_ANY, _process, 1, 1); | add_native_method("process", Method::CT_ANY, _process, 1, 2); |
| // ^rem{code} | // ^rem{code} |
| add_native_method("rem", Method::CT_ANY, _rem, 1, 10000); | add_native_method("rem", Method::CT_ANY, _rem, 1, 10000); |
| Line 701 MOP::MOP(Pool& apool) : Methoded(apool), | Line 710 MOP::MOP(Pool& apool) : Methoded(apool), |
| // constructor & configurator | // constructor & configurator |
| Methoded *MOP_create(Pool& pool) { | VClass& VClassMAIN_create(Pool& pool) { |
| return new(pool) MOP(pool); | return *new(pool) VClassMAIN(pool); |
| } | |
| void MOP::configure_user(Request& r) { | |
| Pool& pool=r.pool(); | |
| // $MAIN:SQL.drivers | |
| if(Value *sql=r.main_class->get_element(main_sql_name)) | |
| if(Value *element=sql->get_element(main_sql_drivers_name)) | |
| if(Table *protocol2library=element->get_table()) | |
| r.classes_conf.put(name(), protocol2library); | |
| } | } |