|
|
| version 1.214, 2013/07/23 14:29:01 | version 1.224, 2015/10/26 01:21:54 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: parser @b operators. | Parser: parser @b operators. |
| Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| */ | */ |
| Line 27 volatile const char * IDENT_OP_C="$Id$"; | Line 27 volatile const char * IDENT_OP_C="$Id$"; |
| // defines | // defines |
| #define CASE_DEFAULT_VALUE "DEFAULT" | #define CASE_DEFAULT_VALUE "DEFAULT" |
| #define PROCESS_MAIN_OPTION_NAME "main" | |
| #define PROCESS_FILE_OPTION_NAME "file" | |
| #define PROCESS_LINENO_OPTION_NAME "lineno" | |
| // class | // class |
| Line 64 static const String exception_var_name(E | Line 61 static const String exception_var_name(E |
| class Untaint_lang_name2enum: public HashString<String::Language> { | class Untaint_lang_name2enum: public HashString<String::Language> { |
| public: | public: |
| Untaint_lang_name2enum() { | Untaint_lang_name2enum() { |
| #define ULN(name, LANG) \ | #define ULN(name, LANG) put(name, (value_type)(String::L_##LANG)); |
| put(String::Body(name), (value_type)(String::L_##LANG)); | |
| ULN("clean", CLEAN); | ULN("clean", CLEAN); |
| ULN("as-is", AS_IS); | ULN("as-is", AS_IS); |
| ULN("optimized-as-is", AS_IS|String::L_OPTIMIZE_BIT); | ULN("optimized-as-is", AS_IS|String::L_OPTIMIZE_BIT); |
| Line 178 static void _process(Request& r, MethodP | Line 174 static void _process(Request& r, MethodP |
| const String* main_alias=0; | const String* main_alias=0; |
| const String* file_alias=0; | const String* file_alias=0; |
| int line_no_alias_offset=0; | int line_no_alias_offset=0; |
| bool allow_class_replace=false; | |
| size_t options_index=index+1; | size_t options_index=index+1; |
| if(options_index<params.count()) | if(options_index<params.count()) |
| if(HashStringValue* options=params.as_hash(options_index)) { | if(HashStringValue* options=params.as_hash(options_index)) { |
| int valid_options=0; | int valid_options=0; |
| if(Value* vmain_alias=options->get(PROCESS_MAIN_OPTION_NAME)) { | for(HashStringValue::Iterator i(*options); i; i.next() ){ |
| valid_options++; | |
| main_alias=&vmain_alias->as_string(); | String::Body key=i.key(); |
| } | Value* value=i.value(); |
| if(Value* vfile_alias=options->get(PROCESS_FILE_OPTION_NAME)) { | |
| valid_options++; | if(key == "main") { |
| file_alias=&vfile_alias->as_string(); | valid_options++; |
| } | main_alias=&value->as_string(); |
| if(Value* vline_no_alias_offset=options->get(PROCESS_LINENO_OPTION_NAME)) { | } else if(key == "file") { |
| valid_options++; | valid_options++; |
| line_no_alias_offset=vline_no_alias_offset->as_int(); | file_alias=&value->as_string(); |
| } else if(key == "lineno") { | |
| valid_options++; | |
| line_no_alias_offset=value->as_int(); | |
| } else if(key == "replace") { | |
| valid_options++; | |
| allow_class_replace=r.process_to_value(*value).as_bool(); | |
| } | |
| } | } |
| if(valid_options!=options->count()) | if(valid_options!=options->count()) |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); |
| } | } |
| Line 207 static void _process(Request& r, MethodP | Line 212 static void _process(Request& r, MethodP |
| Value& vjunction=params.as_junction(index, "body must be code"); | Value& vjunction=params.as_junction(index, "body must be code"); |
| // evaluate source to process | // evaluate source to process |
| const String& source=r.process_to_string(vjunction); | const String& source=r.process_to_string(vjunction); |
| Temp_class_replace class_replace(r, allow_class_replace); | |
| r.use_buf(*target_class, | r.use_buf(*target_class, |
| source.untaint_cstr(String::L_AS_IS, r.connection(false)), | source.untaint_cstr(String::L_AS_IS, r.connection(false)), |
| main_alias, | main_alias, |
| Line 288 static void _while(Request& r, MethodPar | Line 296 static void _while(Request& r, MethodPar |
| static void _use(Request& r, 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); |
| bool allow_class_replace=false; | |
| if(params.count()==2) | |
| if(HashStringValue* options=params.as_hash(1)) { | |
| int valid_options=0; | |
| for(HashStringValue::Iterator i(*options); i; i.next() ){ | |
| String::Body key=i.key(); | |
| Value* value=i.value(); | |
| if(key == "replace") { | |
| valid_options++; | |
| allow_class_replace=r.process_to_value(*value).as_bool(); | |
| } | |
| if(valid_options!=options->count()) | |
| throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); | |
| } | |
| } | |
| Temp_class_replace class_replace(r, allow_class_replace); | |
| // _use could be called from the parser3 method only, so caller is always defined | // _use could be called from the parser3 method only, so caller is always defined |
| r.use_file(r.main_class, vfile.as_string(), r.get_method_filename(&r.get_method_frame()->caller()->method)); | r.use_file(r.main_class, vfile.as_string(), r.get_method_filename(&r.get_method_frame()->caller()->method)); |
| } | } |
| Line 326 static void _for(Request& r, MethodParam | Line 356 static void _for(Request& r, MethodParam |
| VInt* vint=new VInt(0); | VInt* vint=new VInt(0); |
| VMethodFrame& caller=*r.get_method_frame()->caller(); | VMethodFrame& caller=*r.get_method_frame()->caller(); |
| caller.put_element(var_name, vint, false); | r.put_element(caller, var_name, vint); |
| if(delim_maybe_code){ // delimiter set | if(delim_maybe_code){ // delimiter set |
| bool need_delim=false; | bool need_delim=false; |
| Line 435 public: | Line 465 public: |
| #endif | #endif |
| static void _switch(Request& r, MethodParams& params) { | static void _switch(Request& r, MethodParams& params) { |
| Switch_data* data=new Switch_data(r, r.process_to_value(params[0])); | Switch_data* data=new Switch_data(r, r.process_to_value(params[0])); |
| Temp_hash_value<const String::Body, void*> | Temp_hash_value<HashString<void*>, void*> switch_data_setter(&r.classes_conf, switch_data_name, data); |
| switch_data_setter(r.classes_conf, switch_data_name, data); | |
| Value& cases_code=params.as_junction(1, "switch cases must be code"); | Value& cases_code=params.as_junction(1, "switch cases must be code"); |
| // execution of found ^case[...]{code} must be in context of ^switch[...]{code} | // execution of found ^case[...]{code} must be in context of ^switch[...]{code} |
| Line 525 static Try_catch_result try_catch(Reques | Line 554 static Try_catch_result try_catch(Reques |
| Value* method_frame=junction->method_frame; | Value* method_frame=junction->method_frame; |
| Value* saved_exception_var_value=method_frame->get_element(exception_var_name); | Value* saved_exception_var_value=method_frame->get_element(exception_var_name); |
| VMethodFrame& frame=*junction->method_frame; | VMethodFrame& frame=*junction->method_frame; |
| frame.put_element(exception_var_name, &details.vhash, false); | frame.put_element(exception_var_name, &details.vhash); |
| result.processed_code=r.process(*catch_code); | result.processed_code=r.process(*catch_code); |
| // retriving $exception.handled, restoring $exception var | // retriving $exception.handled, restoring $exception var |
| Value* vhandled=details.vhash.hash().get(exception_handled_part_name); | Value* vhandled=details.vhash.hash().get(exception_handled_part_name); |
| frame.put_element(exception_var_name, saved_exception_var_value, false); | frame.put_element(exception_var_name, saved_exception_var_value); |
| bool bhandled=false; | bool bhandled=false; |
| if(vhandled) { | if(vhandled) { |
| Line 555 static Try_catch_result try_catch(Reques | Line 584 static Try_catch_result try_catch(Reques |
| return result; | return result; |
| } | } |
| static StringOrValue process_try_body_code(Request& r, Value* body_code) { | |
| return r.process(*body_code); | |
| } | |
| // cache-- | // cache-- |
| // consts | // consts |
| Line 616 static void locked_process_and_cache_put | Line 649 static void locked_process_and_cache_put |
| } else | } else |
| throw Exception(PARSER_RUNTIME, | throw Exception(PARSER_RUNTIME, |
| result.exception_should_be_handled, | result.exception_should_be_handled, |
| "$"EXCEPTION_VAR_NAME"."EXCEPTION_HANDLED_PART_NAME" value must be " | "$" EXCEPTION_VAR_NAME "." EXCEPTION_HANDLED_PART_NAME " value must be " |
| "either boolean or string '"CACHE_EXCEPTION_HANDLED_CACHE_NAME"'"); | "either boolean or string '" CACHE_EXCEPTION_HANDLED_CACHE_NAME "'"); |
| } else | } else |
| info.processed_code=&result.processed_code.as_string(); | info.processed_code=&result.processed_code.as_string(); |
| Line 695 static time_t as_expires(Request& r, Met | Line 728 static time_t as_expires(Request& r, Met |
| int index, time_t now) { | int index, time_t now) { |
| time_t result; | time_t result; |
| if(Value* vdate=params[index].as(VDATE_TYPE)) | if(Value* vdate=params[index].as(VDATE_TYPE)) |
| result=static_cast<VDate*>(vdate)->get_time(); | result=(time_t)(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 709 static void _cache(Request& r, MethodPar | Line 742 static void _cache(Request& r, MethodPar |
| // ^cache[] -- return current expiration time | // ^cache[] -- return current expiration time |
| Cache_scope* scope=static_cast<Cache_scope*>(r.classes_conf.get(cache_data_name)); | Cache_scope* scope=static_cast<Cache_scope*>(r.classes_conf.get(cache_data_name)); |
| if(!scope) | if(!scope) |
| throw Exception(PARSER_RUNTIME, | throw Exception(PARSER_RUNTIME, 0, "expire-time get without cache"); |
| 0, | r.write_no_lang(*new VDate((pa_time_t)scope->expires)); |
| "expire-time get without cache"); | |
| r.write_no_lang(*new VDate(scope->expires)); | |
| return; | return; |
| } | } |
| Line 747 static void _cache(Request& r, MethodPar | Line 778 static void _cache(Request& r, MethodPar |
| Cache_scope scope={as_expires(r, params, 1, now), 0}; | Cache_scope scope={as_expires(r, params, 1, now), 0}; |
| Temp_hash_value<const String::Body, void*> | Temp_hash_value<HashString<void*>, void*> cache_scope_setter(&r.classes_conf, cache_data_name, &scope); |
| cache_scope_setter(r.classes_conf, cache_data_name, &scope); | |
| Value& body_code=params.as_junction(2, "body_code must be code"); | Value& body_code=params.as_junction(2, "body_code must be code"); |
| Value* catch_code=0; | Value* catch_code=0; |
| if(params.count()>3) | if(params.count()>3) |
| Line 785 static void _cache(Request& r, MethodPar | Line 815 static void _cache(Request& r, MethodPar |
| cache_delete(file_spec); | cache_delete(file_spec); |
| } | } |
| // process without cacheing | // process without caching |
| const String& processed_body=r.process_to_string(body_code); | if(catch_code){ |
| // write it out | Try_catch_result result=try_catch(r, process_try_body_code, &body_code, catch_code); |
| r.write_assign_lang(processed_body); | r.write_assign_lang(result.processed_code); |
| } else { | |
| const String& processed_body=r.process_to_string(body_code); | |
| r.write_assign_lang(processed_body); | |
| } | |
| } | } |
| static StringOrValue process_try_body_code(Request& r, Value* body_code) { | |
| return r.process(*body_code); | |
| } | |
| static void _try_operator(Request& r, MethodParams& params) { | static void _try_operator(Request& r, MethodParams& params) { |
| Value& body_code=params.as_junction(0, "body_code must be 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"); | Value& catch_code=params.as_junction(1, "catch_code must be code"); |
| Line 802 static void _try_operator(Request& r, Me | Line 833 static void _try_operator(Request& r, Me |
| Try_catch_result result; | Try_catch_result result; |
| StringOrValue finally_result; | StringOrValue finally_result; |
| try{ | try{ |
| result=try_catch(r, | result=try_catch(r, process_try_body_code, &body_code, &catch_code); |
| 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"); | |
| } catch(...){ | } catch(...){ |
| if(finally_code) | if(finally_code) |
| finally_result=r.process(*finally_code); | finally_result=r.process(*finally_code); |
| Line 891 VClassMAIN::VClassMAIN(): VClass() { | Line 916 VClassMAIN::VClassMAIN(): VClass() { |
| // ^apply-taint[untaint lang][string] | // ^apply-taint[untaint lang][string] |
| add_native_method("apply-taint", Method::CT_ANY, _apply_taint, 1, 2, Method::CO_WITHOUT_FRAME); | add_native_method("apply-taint", Method::CT_ANY, _apply_taint, 1, 2, Method::CO_WITHOUT_FRAME); |
| // ^process[code] | // ^process{code} |
| // ^process[context]{code}[options hash] | |
| add_native_method("process", Method::CT_ANY, _process, 1, 3); | add_native_method("process", Method::CT_ANY, _process, 1, 3); |
| // ^rem{code} | // ^rem{code} |
| Line 900 VClassMAIN::VClassMAIN(): VClass() { | Line 926 VClassMAIN::VClassMAIN(): VClass() { |
| // ^while(condition){code} | // ^while(condition){code} |
| add_native_method("while", Method::CT_ANY, _while, 2, 3, Method::CO_WITHOUT_FRAME); | add_native_method("while", Method::CT_ANY, _while, 2, 3, Method::CO_WITHOUT_FRAME); |
| // ^use[file] | // ^use[file[;options hash]] |
| add_native_method("use", Method::CT_ANY, _use, 1, 1); | add_native_method("use", Method::CT_ANY, _use, 1, 2); |
| // ^break[] | // ^break[] |
| add_native_method("break", Method::CT_ANY, _break, 0, 0, Method::CO_WITHOUT_FRAME); | add_native_method("break", Method::CT_ANY, _break, 0, 0, Method::CO_WITHOUT_FRAME); |