Roxen.git / server / etc / modules / RXML.pmod / module.pmod

version» Context lines:

Roxen.git/server/etc/modules/RXML.pmod/module.pmod:1:   // RXML parser and compiler framework.   //   // Created 1999-07-30 by Martin Stjernholm.   // - // $Id: module.pmod,v 1.431 2012/05/11 00:23:04 mast Exp $ + // $Id$      // Kludge: Must use "RXML.refs" somewhere for the whole module to be   // loaded correctly.   protected object Roxen;   protected object roxen;      //! API stability notes:   //!   //! The API in this file regarding the global functions and the Tag,   //! TagSet, Context, Frame and Type classes and their descendants is
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:154:   // remove items that aren't refcounted (and strings).      protected Thread.Mutex all_tag_sets_mutex = Thread.Mutex();      #define LOOKUP_TAG_SET(owner, name) ((all_tag_sets[owner] || ([]))[name])      // Assumes all_tag_sets_mutex is locked.   #define SET_TAG_SET(owner, name, value) do { \    mapping(string:TagSet|int) map = \    all_tag_sets[owner] || \ -  (all_tag_sets[owner] = set_weak_flag (([]), 1)); \ +  (all_tag_sets[owner] = set_weak_flag (([]), Pike.WEAK_VALUES)); \    map[name] = (value); \   } while (0)      protected mapping(string:program/*(Parser)*/) reg_parsers = ([]);   // Maps each parser name to the parser program.      protected mapping(string:Type) reg_types = ([]);   // Maps each type name to a type object with the PNone parser.    - protected mapping(mixed:string) reverse_constants = set_weak_flag (([]), 1); + protected mapping(mixed:string) reverse_constants = +  set_weak_flag (([]), Pike.WEAK_INDICES);         // Interface classes      class Tag   //! Interface class for the static information about a tag.   {    constant is_RXML_Tag = 1;    constant is_RXML_encodable = 1;    constant is_RXML_p_code_frame = 1;
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:456:       final mixed handle_tag (TagSetParser parser, mapping(string:string) args,    void|string content)    // Callback for tag set parsers to handle tags. Note that this    // function handles an unwind frame for the parser.    {    // Note: args may be zero when this is called for PI tags.    Context ctx = parser->context;    object/*(Frame)HMM*/ frame;    MAKE_FRAME (frame, ctx, parser, args, content); -  if (!zero_type (frame->raw_tag_text)) +  if (object_variablep(frame, "raw_tag_text"))    frame->raw_tag_text = parser->raw_tag_text();    mixed result;    EVAL_FRAME (frame, ctx, parser, parser->type, result);    return result;    }       final array _p_xml_handle_tag (object/*(PXml)*/ parser, mapping(string:string) args,    void|string content)    {    Type type = parser->type;    parser->drain_output();    Context ctx = parser->context;    object/*(Frame)HMM*/ frame;    MAKE_FRAME (frame, ctx, parser, args, content); -  if (!zero_type (frame->raw_tag_text)) +  if (object_variablep (frame, "raw_tag_text"))    frame->raw_tag_text = parser->current_input();    mixed result;    EVAL_FRAME (frame, ctx, parser, type, result);    if (result != nil && result != empty) parser->add_value (result);    return ({});    }       final array _p_xml_handle_pi_tag (object/*(PXml)*/ parser, string content)    {    Type type = parser->type;    parser->drain_output();    -  sscanf (content, "%[ \t\n\r]%s", string ws, string rest); -  if (ws == "" && rest != "") { +  if (sizeof(content) && !(< ' ', '\t', '\n', '\r' >)[content[0]]) {    // The parser didn't match a complete name, so this is a false    // alarm for an unknown PI tag.    if (!type->free_text)    return utils->unknown_pi_tag_error (parser, content);    return 0;    }       Context ctx = parser->context;    object/*(Frame)HMM*/ frame;    MAKE_FRAME (frame, ctx, parser, 0, content); -  if (!zero_type (frame->raw_tag_text)) +  if (object_variablep (frame, "raw_tag_text"))    frame->raw_tag_text = parser->current_input();    mixed result;    EVAL_FRAME (frame, ctx, parser, type, result);    if (result != nil && result != empty) parser->add_value (result);    return ({});    }       mapping(string:mixed) _eval_splice_args (Context ctx,    mapping(string:string) raw_args,    mapping(string:Type) my_req_args)
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:1347:    inherit TagSet;       protected void create (TagSet... tag_sets)    {    // Note: Some code duplication wrt TagSet.create.    id_number = ++tag_set_count;   #ifdef RXML_OBJ_DEBUG    __object_marker->create (this_object());   #endif    // Make sure TagSet::`-> gets called. -  this->imported = tag_sets; +  TagSet me = this; +  me->imported = tag_sets;    }       string _sprintf (void|int flag)    {    if (flag != 'O') return 0;    return "RXML.CompositeTagSet(" + tag_set_component_names() + ")" +    OBJ_COUNT;    //return "RXML.TagSet(" + id_number + ")" + OBJ_COUNT;    }   }
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:1444:    mixed val = rxml_const_eval (ctx, var, scope_name);    // We replace the variable object with the evaluated value when    // rxml_const_eval is used. However, we hide    // ctx->misc->recorded_changes so that the setting isn't cached.    // That since rxml_const_eval should work for values that only are    // constant in the current request. Note that we can still    // overcache the returned result; it's up to the user to avoid    // that with suitable cache tags.    array rec_chgs = ctx->misc->recorded_changes;    ctx->misc->recorded_changes = 0; -  ctx->set_var(var, val, scope_name); +  // NB: The scope name may contain dot-separated segments. +  ctx->user_set_var(var, val, scope_name);    ctx->misc->recorded_changes = rec_chgs;    return type ? type->encode (val) : val;    }       mixed rxml_const_eval (Context ctx, string var, string scope_name);    //! If the variable value is the same throughout the life of the    //! context, this method can be used instead of @[rxml_var_eval] to    //! only get a call the first time the value is evaluated.    //!    //! Note that this doesn't provide any control over the type
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:1712:    string coded = replace (var, "\0", "\0\0");    if (coded != var)    splitted = map (replace (coded, "..", "\0p") / ".",    replace, ({"\0p", "\0\0"}), ({".", "\0"}));    else    splitted = map (replace (var, "..", "\0") / ".", replace, "\0", ".");    }    else    splitted = var / ".";    -  if (stringp (scope_name)) +  if (stringp (scope_name)) { +  if (has_value(scope_name, ".")) { +  // We need to split the scope name. +  splitted = parse_user_var(scope_name, "_")[1..] + splitted; +  } else {    splitted = ({scope_name}) + splitted; -  else if (sizeof (splitted) == 1) -  splitted = ({scope_name || "_"}) + splitted; +  } +  } else if (sizeof (splitted) == 1) +  splitted = ({ "_" }) + splitted;       for (int i = 2; i < sizeof (splitted); i++)    if (sscanf (splitted[i], "%d%*c", int d) == 1) splitted[i] = d;       return splitted;    }       local mixed get_var (string|array(string|int) var, void|string scope_name,    void|Type want_type)    //! Returns the value of the given variable in the specified scope,
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:2197:    //! the given arguments is added to it, so that it will be called    //! when the result p-code is reevaluated.    //!    //! If @[callback] is a string then it's taken to be the name of a    //! function to call in the current @[id] object. The string can    //! also contain "->" to build index chains. E.g. the string    //! "misc->foo->bar" will cause a call to @[id]->misc->foo->bar()    //! when the result p-code is evaluated.    {    if (misc->recorded_changes) -  // See PCode.process_recorded_changes for details. +  // See PCode.low_process_recorded_changes for details.    misc->recorded_changes += ({callback, args, ([])});    }       protected int last_internal_var_id = 0;       string alloc_internal_var()    //! Allocates and returns a unique variable name in the special    //! scope "_internal_", creating that scope if necessary. After this    //! it's safe to use that variable for internal purposes in tags    //! with the normal variable functions. No other variables in the
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:2591:    //! @endignore       string _sprintf (int flag)    {    return flag == 'O' &&    ((function_name (object_program (this)) || "RXML.Context") +    "()" + OBJ_COUNT);    }      #ifdef MODULE_DEBUG - #if constant (thread_create) +     Thread.Thread in_use; - #else -  int in_use; +    #endif - #endif +    }      /*protected*/ class CacheStaticFrame (string scope_name)   // This class is used when tracking local scopes in frames that have   // been optimized away by FLAG_IS_CACHE_STATIC. It contains the scope   // name and is used as the key for Context.enter_scope and   // Context.leave_scope.   //   // Can't be protected since encode_value must be able to index it.   {
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:2888:      final Context get_context() {return [object(Context)] RXML_CONTEXT;}   //! Returns the current @[RXML.Context] object, which contains all the   //! evaluation context info. It's updated before any function in   //! @[RXML.Tag] or @[RXML.Frame] is called.   //!   //! @note   //! A slightly faster way to access it is through the @[RXML_CONTEXT]   //! macro in @tt{module.h@}.    - #if defined (MODULE_DEBUG) && constant (thread_create) + #if defined (MODULE_DEBUG)      // Got races in this debug check, but looks like we have to live with that. :/      #define ENTER_CONTEXT(ctx) \    Context __old_ctx = RXML_CONTEXT; \    SET_RXML_CONTEXT (ctx); \    if (ctx) { \    if (ctx->in_use && ctx->in_use != this_thread()) \    fatal_error ("Attempt to use context asynchronously.\n"); \    ctx->in_use = this_thread(); \
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:3651:    //! definition, if any exists. It either returns a frame from the    //! overridden tag or, if no overridden tag exists, a string    //! containing a formatted tag (which requires that the result type    //! supports formatted tags, i.e. has a working @[format_tag]    //! function). If @[args] and @[content] are given, they will be    //! used in the tag after parsing, otherwise the @[raw_tag_text]    //! variable is used, which must have a string value.    {   #ifdef MODULE_DEBUG   #define CHECK_RAW_TEXT \ -  if (zero_type (this_object()->raw_tag_text)) \ +  if (!object_variablep (this, "raw_tag_text")) \    fatal_error ("The variable raw_tag_text must be defined.\n"); \    if (!stringp (this_object()->raw_tag_text)) \    fatal_error ("raw_tag_text must have a string value.\n");   #else   #define CHECK_RAW_TEXT   #endif    // FIXME: This assumes an xml-like parser.       if (object(Tag) overridden = get_overridden_tag()) {    Frame frame;
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:4231:    if (t->parser_prog != PNone) {    sub_p_code->create (t, ctx, ctx_tag_set, 0, comp);    Parser parser = t->get_parser (ctx, ctx_tag_set, 0, sub_p_code);    THIS_TAG_DEBUG ("Evaluating and compiling "    "argument value %s with %O\n",    format_short (val), parser);    parser->finish (val); // Should not unwind.    mixed v = parser->eval(); // Should not unwind.    t->give_back (parser, ctx_tag_set);    +  if ((v != nil) && t->type_check) t->type_check(v); +  // FIXME: Add type-checking to the compiled code as well. +     if (t->sequential)    fn_text_add (sprintf ("args[%O] = %s;\n", arg,    sub_p_code->compile_text (comp)));    else    fn_text_add (    "tmp=", sub_p_code->compile_text (comp), ";\n",    sprintf ("if (tmp == RXML.nil)"    " set_nil_arg(args,%O,%s,%s,ctx->id);\n"    "else args[%O] = tmp;\n",    arg, comp->bind (t), req_args_var, arg));
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:4378:   #endif       //! Frame cleanup callback.    //!    //! Overload this function with code to cleanup    //! any state that shouldn't be kept for the next    //! use of the frame.    //!    //! This function is called after @[do_return()],    //! and also during exception processing. -  static void cleanup() {} +  protected void cleanup() {}       mixed _eval (Context ctx, TagSetParser|PCode evaler, Type type)    // Note: It might be somewhat tricky to override this function,    // since it handles unwinding and rewinding.    {    RequestID id = ctx->id;    PikeCompile comp;       // Unwind state data:   #define EVSTAT_NONE 0
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:6145:    //! Returns a type identical to this one, but which has the given    //! parser. parser_args is passed as extra arguments to the    //! create()/reset()/clone() functions.    {    TDEBUG_MSG ("%O(%s%{, %O%})", this_object(), newparser->name, parser_args);    Type newtype;    if (sizeof (parser_args)) { // Can't cache this.    newtype = clone();    newtype->parser_prog = newparser;    newtype->parser_args = parser_args; -  if (newparser->tag_set_eval) newtype->_p_cache = set_weak_flag (([]), 1); +  if (newparser->tag_set_eval) +  newtype->_p_cache = set_weak_flag (([]), Pike.WEAK_INDICES);    TDEBUG_MSG (" got args, can't cache\n");    }    else {    if (!_t_obj_cache) _t_obj_cache = ([]);    if (!(newtype = _t_obj_cache[newparser]))    if (newparser == parser_prog) {    _t_obj_cache[newparser] = newtype = this_object();    TDEBUG_MSG (" caching and returning this object\n");    }    else {    _t_obj_cache[newparser] = newtype = clone();    newtype->parser_prog = newparser; -  if (newparser->tag_set_eval) newtype->_p_cache = set_weak_flag (([]), 1); +  if (newparser->tag_set_eval) +  newtype->_p_cache = set_weak_flag (([]), Pike.WEAK_INDICES);    TDEBUG_MSG (" returning cloned type %O\n", newtype);    }    else    TDEBUG_MSG (" returning %O from cache\n", newtype);    }   #ifdef DEBUG    if (reg_types[this_object()->name]->parser_prog != PNone)    error ("Incorrect type object registered in reg_types.\n");   #endif    return newtype;
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:7436:    case TString.name: case TAnyText.name: case local::name: return [string] val;    default: return [string] indirect_convert (val, from);    case TScalar.name:    }    mixed err = catch {return (string) val;};    parse_error ("Cannot convert %s to %s: %s",    format_short (val), name, describe_error (err));    }   }    + TNarrowText t_narrowtext = TNarrowText(); + //! The type for plain text that needs to be narrow (eg HTTP headers + //! and similar). +  + //! + //! @seealso + //! @[t_narrowtext] + class TNarrowText + { +  inherit TText; +  constant name = "text/x-8bit"; +  constant type_name = "RXML.t_narrowtext"; +  Type supertype = t_text; +  Type conversion_type = t_text; +  +  void type_check(mixed val, void|string msg, mixed ... args) +  { +  ::type_check(val); +  if (stringp(val) && (String.width(val) > 8)) { +  type_check_error(msg, args, "Got wide string where 8-bit string required.\n"); +  } +  } + } +    TXml t_xml = TXml();   //! The type for XML and similar markup.      //!   //! @seealso   //! @[t_xml]   class TXml   {    inherit TText;    constant name = "text/xml";
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8272:   protected class PikeCompile   //! Helper class to paste together a Pike program from strings. This   //! is thread safe.   {   #ifdef DEBUG    protected string pcid = "pc" + ++p_comp_count;   #endif    protected inherit Thread.Mutex: mutex;       // These are covered by the mutex. +  +  //! Buffer where the generated code is collected.    protected inherit String.Buffer: code; -  +  +  //! Mapping where the indices are symbols that are +  //! declared in @[code]. These will be migrated to +  //! @[bindings] by @[compile()].    protected mapping(string:int) cur_ids = ([]); -  +  +  //! Mapping from indexable object to symbol. +  //! +  //! When added to the mapping @expr{obj[sym]@} is a string +  //! that is the name of the corresponding symbol in @[bindings]. +  //! +  //! When the symbol is resolved @expr{obj[sym]@} will be set +  //! to @expr{bindings[obj[sym]]@}. +  //! +  //! @seealso +  //! @[delayed_resolve()]    protected mapping(mixed:mixed) delayed_resolve_places = ([]);    -  +  //! Mapping from symbol name to resolved value. +  //! +  //! @seealso +  //! @[bind()]    protected mapping(string:mixed) bindings = ([    // Prepopulate with standard things we need access to.    "set_nil_arg": set_nil_arg,    ]);    -  +  //! Bind the value to a symbol. +  //! +  //! @param val +  //! Value to bind. +  //! +  //! Generates a new unique symbol name, and binds it to @[val]. +  //! +  //! @returns +  //! Returns the generated symbol name. +  //! +  //! @seealso +  //! @[bindings]    string bind (mixed val)    {    string id =   #ifdef DEBUG    pcid +   #endif    "b" + p_comp_idnr++;    COMP_MSG ("%O bind %O to %s\n", this_object(), val, id);    bindings[id] = val;    return id;    }    -  string add_var (string type, void|string init) -  { -  string id = - #ifdef DEBUG -  pcid + - #endif -  "v" + p_comp_idnr++; -  string txt; -  -  if (init) { -  COMP_MSG ("%O add var: %s %s = %O\n", this_object(), type, id, init); -  txt = sprintf ("%s %s = %s;\n", type, id, init); -  } -  else { -  COMP_MSG ("%O add var: %s %s\n", this_object(), type, id); -  txt = sprintf ("%s %s;\n", type, id); -  } -  -  Thread.MutexKey lock = mutex::lock(); -  code::add (txt); -  cur_ids[id] = 1; -  -  return id; -  } -  +  //! Generate a function definition. +  //! +  //! @param rettype +  //! Return type for the function. +  //! +  //! @param arglist +  //! Argument list for the function, not including the +  //! outer parenthesis. +  //! +  //! @param def +  //! Body of the function, not including the outer braces. +  //! +  //! Generates a new unique function name, and adds it to @[cur_ids]. +  //! Adds a corresponding function declaration to @[code]. +  //! +  //! @returns +  //! Returns the name of the function. +  //! +  //! @note +  //! May run the compiler synchronously.    string add_func (string rettype, string arglist, string def)    {    string id =   #ifdef DEBUG    pcid +   #endif    "f" + p_comp_idnr++;    COMP_MSG ("%O add func: %s %s (%s)\n{%s}\n",    this_object(), rettype, id, arglist, def); -  string txt = sprintf ( +  string txt = predef::sprintf (    "# 1\n" // Workaround for pike 7.8 bug with large line numbers, [bug 6146].    "%s %s (%s)\n{%s}\n", rettype, id, arglist, def);       Thread.MutexKey lock = mutex::lock();    code::add (txt);    cur_ids[id] = 1;    -  +  // Be nice to the Pike compiler, and compile the code in segments. +  if (code::_sizeof() >= 65536) { +  lock = UNDEFINED; +  compile(); +  } +     return id;    }    -  +  //! Resolve an identifier @[id]. +  //! +  //! @note +  //! May trigger compilation. +  //! +  //! @note +  //! In @tt{DEBUG@} mode errors will be thrown on attempting +  //! to resolve unknown identifiers.    mixed resolve (string id)    {    COMP_MSG ("%O resolve %O\n", this_object(), id);   #ifdef DEBUG    if (!has_prefix (id, pcid))    error ("Resolve id %O does not belong to this object.\n", id);   #endif    if (zero_type (bindings[id])) {    compile();   #ifdef DEBUG    if (zero_type (bindings[id])) error ("Unknown id %O.\n", id);   #endif    }    return bindings[id];    }    -  +  //! Register a variable for later resolution. +  //! +  //! @param what +  //! Object (or similar) that contains the variable to update. +  //! +  //! @param index +  //! Index in @[what] to update at some later time. +  //! +  //! On entry the value @expr{what[index]@} should be a string +  //! being a symbol name of the value to resolve. At resolution +  //! this string will be replaced with the resolved value. +  //! +  //! @note +  //! If the symbol to resolve is already present in @[bindings] +  //! the resolution will be performed directly. +  //! +  //! @note +  //! Only a single delayed resolution may be registered +  //! for each @[what]. +  //! +  //! @seealso +  //! @[delayed_resolve_places]    void delayed_resolve (mixed what, mixed index)    {    Thread.MutexKey lock = mutex::lock();   #ifdef DEBUG    if (!zero_type (delayed_resolve_places[what]))    error ("Multiple indices per thing to delay resolve not handled.\n");    if (!stringp (what[index]) || !has_prefix (what[index], pcid))    error ("Resolve id %O does not belong to this object.\n", what[index]);   #endif    mixed resolved;
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8376:    COMP_MSG ("%O delayed_resolve %O in %s[%O]\n",    this_object(), what[index], format_short (what), index);    }    else {    what[index] = resolved;    COMP_MSG ("%O delayed_resolve immediately %O in %s[%O]\n",    this_object(), what[index], format_short (what), index);    }    }    +  //! Resolver class used to find stuff in @[bindings]. +  //! +  //! @seealso +  //! @[CompilationHandler]    protected class Resolver (object master)    // Can't keep the instantiated Resolver object around since that'd    // introduce a cyclic reference.    {    void compile_error (string file, int line, string err)    {master->compile_error (file, line, err);}       void compile_warning (string file, int line, string err)    {master->compile_warning (file, line, err);}    -  +  //! Resolve symbols from @[bindings].    mixed resolv (string id, void|string file)    {    mixed val;    if (!zero_type (val = bindings[id])) return val;    return master->resolv (id, file);    }    }    -  object compile() +  //! Compile the code buffered so far in @[code]. +  //! +  //! Updates @[bindings] with symbols from @[cur_ids] and +  //! values from the newly compiled code. +  //! +  //! Performs delayed resolution. +  void compile()    {    Thread.MutexKey lock = mutex::lock(); -  object compiled = 0; +        string txt = code::get();       if (txt != "") {    COMP_MSG ("%O compile\n", this_object());    -  +  object compiled = 0; +     txt +=    "mixed _encode() { } void _decode(mixed v) { }\n"    "constant is_RXML_pike_code = 1;\n"    "constant is_RXML_encodable = 1;\n"   #ifdef RXML_OBJ_DEBUG    // Don't want to encode the cloning of RoxenDebug.ObjectMarker    // in the __INIT that is dumped, since that debug might not be    // wanted when the dump is decoded.    "mapping|object __object_marker = " +    bind (RoxenDebug.ObjectMarker ("object(compiled RXML code)")) + ";\n"
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8466:    foreach (delayed_resolve_places; mixed what;) {    mixed index = m_delete (delayed_resolve_places, what);    if (zero_type (bindings[what[index]]))    delayed_resolve_places[what] = index;    else {    what[index] = bindings[what[index]];    COMP_MSG ("%O resolved delayed %O\n", this_object(), what[index]);    }    }    -  return compiled; +  return;    }    -  +  //! Destruction callback.    protected void destroy()    {    compile(); // To clean up delayed_resolve_places.   #ifdef DEBUG    if (sizeof (delayed_resolve_places)) {    string errmsg = "Still got unresolved delayed resolve places:\n";    foreach (delayed_resolve_places; mixed what;) {    mixed index = m_delete (delayed_resolve_places, what); -  errmsg += replace (sprintf (" %O[%O]: %O", what, index, what[index]), +  errmsg += replace (predef::sprintf (" %O[%O]: %O", +  what, index, what[index]),    "\n", "\n ") + "\n";    }    error (errmsg);    }   #endif    }       //! @ignore    MARK_OBJECT;    //! @endignore
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8747:       /*protected*/ int protocol_cache_time;    // The ctx->id->misc->cacheable setting when result collected p-code    // is finished. It's reinstated on entry whenever the p-code is used    // to ensure that the protocol cache doesn't overcache.       PikeCompile p_code_comp;    // This is inherited by nested PCode instances to make the    // compilation units larger.    -  protected void process_recorded_changes (array rec_chgs, Context ctx) +  void process_recorded_changes (array rec_chgs, Context ctx) +  { +  EXPAND_EXEC (sizeof (rec_chgs)); +  low_process_recorded_changes (rec_chgs, ctx); +  } +  +  protected void low_process_recorded_changes (array rec_chgs, Context ctx)    // This processes ctx->misc->recorded_changes, which is used to    // record things besides the frames that need to added to result    // collecting p-code. The format of ctx->misc->recorded_changes    // might change at any time. Currently it's like this:    //    // ctx->misc->recorded_changes is an array concatenated by any of    // the following sequences:    //    // ({mapping vars})    // The mapping contains various variable and scope changes. Its
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8821:    if (flags & COLLECT_RESULTS) {    PCODE_MSG ("adding result value %s\n", format_short (evaled_value));    if (ctx->misc[" _ok"] != ctx->misc[" _prev_ok"])    // Special case: Poll for changes of the _ok flag, to avoid    // widespread compatibility issues with the existing tags.    ctx->set_misc (" _ok", ctx->misc[" _prev_ok"] = ctx->misc[" _ok"]);    array rec_chgs = ctx->misc->recorded_changes;    EXPAND_EXEC (1 + sizeof (rec_chgs));    exec[length++] = evaled_value;    if (!equal (rec_chgs, ({([])}))) { -  process_recorded_changes (rec_chgs, ctx); +  low_process_recorded_changes (rec_chgs, ctx);    ctx->misc->recorded_changes = ({([])});    }    }    else {    PCODE_MSG ("adding entry %s\n", format_short (entry));    EXPAND_EXEC (1);    exec[length++] = entry;    }       if (p_code) p_code->add (ctx, entry, evaled_value);
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8882:    // the frame unevaluated.    frame_flags |= FLAG_DONT_CACHE_RESULT;    PCODE_MSG ("frame %O not result cached due to exception\n", frame);    break add_evaled_value;    }    PCODE_MSG ("adding result of frame %O: %s\n",    frame, format_short (evaled_value));    EXPAND_EXEC (1 + sizeof (rec_chgs));    exec[length++] = evaled_value;    if (!equal (rec_chgs, ({([])}))) -  process_recorded_changes (rec_chgs, ctx); +  low_process_recorded_changes (rec_chgs, ctx);    break add_frame;    }    }    }       EXPAND_EXEC (3);    exec[length] = frame->tag || frame; // To make new frames from.   #ifdef DEBUG    if (!stringp (frame->args) && !functionp (frame->args) &&    (frame_flags & FLAG_PROC_INSTR ? frame->args != 0 : !mappingp (frame->args)))
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:8977:    }       // Install any trailing variable changes. This is useful to    // catch the last scope leave from a FLAG_IS_CACHE_STATIC    // optimized frame. With the compaction below it'll therefore    // often erase an earlier stored scope.    array rec_chgs = ctx->misc->recorded_changes;    ctx->misc->recorded_changes = ({([])});    if (sizeof (rec_chgs)) {    EXPAND_EXEC (sizeof (rec_chgs)); -  process_recorded_changes (rec_chgs, ctx); +  low_process_recorded_changes (rec_chgs, ctx);    }       if (!(flags & CTX_ALREADY_GOT_VC))    m_delete (RXML_CONTEXT->misc, "recorded_changes");    PCODE_MSG ("end result collection\n");      #ifndef DISABLE_RXML_COMPACT    if (length > 0) {    // Collapse sequences of constants and VariableChange's, etc.    // This is actually a simple peephole optimizer. Could be done
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:9207:       PCODE_MSG ((p_code_comp ?    sprintf ("evaluating partially resolved p-code %O, using "    "resolver %O\n", this_object(), p_code_comp) :    sprintf ("evaluating completely resolved p-code %O\n",    this_object())));       while (1) { // Loops only if errors are catched.    mixed item;    Frame frame; +  +  // Don't want to leak recorded changes to the surrounding scope, +  // so we'll replace it with an empty one. The original is +  // restored before return below. +  array saved_recorded_changes = ctx->misc->recorded_changes; +  ctx->misc->recorded_changes = ({([])}); +     if (mixed err = catch {       if (p_code_comp) {    p_code_comp->compile();    if (flags & FINISHED) p_code_comp = 0;    }       for (; pos < length; pos++) {    item = exec[pos];   
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:9308:    ctx->eval_finish (1);    ctx->id->eval_status["rxmlpcode"] = 1;       if (ctx->state_updated > update_count) {    PCODE_UPDATE_MSG ("%O (ctx %O): Marked as updated due to "    "ctx->state_updated %d > %d.\n", this, ctx,    ctx->state_updated, update_count);    flags |= UPDATED;    }    +  if (new_p_code) { +  array rec_chgs = ctx->misc->recorded_changes; +  if (sizeof (rec_chgs)) { +  new_p_code->process_recorded_changes (rec_chgs, ctx); +  } +  } +  ctx->misc->recorded_changes = saved_recorded_changes; +     if (!ppos)    return type->sequential ? type->copy_empty_value() : nil;    else    if (type->sequential)    return `+ (type->empty_value, @parts[..ppos - 1]);    else    if (ppos != 1) return utils->get_non_nil (type, @parts[..ppos - 1]);    else return parts[0];       }) { -  +  ctx->misc->recorded_changes = saved_recorded_changes; +     if (objectp (err) && ([object] err)->thrown_at_unwind) {    ctx->unwind_state[this_object()] = ({err, pos, parts, ppos});    throw (this_object());    }       else {    PCODE_UPDATE_MSG (    "%O (item %O): Restoring p-code update count "    "from %d to %d since the frame is stored unevaluated "    "due to exception.\n",
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:9549:    frame->__object_marker->create (frame);   #endif    }    else    exec[pos + 1] = frame = item->_clone_empty();    frame->_restore (exec[pos + 2]);    pos += 2;    }    }    } +  +  array(mixed) collect_things_recur() +  { +  // Note: limit is on visited nodes, not resulting entries. Don't +  // raise above 100k without considering the stack limit below. +  constant limit = 10000; +  +  ADT.Queue queue = ADT.Queue(); +  mapping(mixed:int) visited = ([]); +  +  queue->write (this); +  +  for (int i = 0; sizeof (queue) && i < limit; i++) { +  mixed entry = queue->read(); +  +  if (functionp (entry) || intp (entry) || visited[entry] || floatp (entry)) +  continue; +  +  if (objectp(entry) && (entry->is_RXML_Type || entry->is_RXML_Tag)) +  continue; +  +  visited[entry] = 1; +  +  if (arrayp (entry) || mappingp (entry) || multisetp (entry)) { +  foreach (entry; mixed ind; mixed val) { +  if (!arrayp (entry)) +  queue->write (ind); +  if (!multisetp (entry)) +  queue->write (val);    } -  +  } else if (objectp (entry)) { +  if (entry->is_RXML_PCode) +  queue->write (entry->exec); +  } +  }    -  + #ifdef DEBUG +  if (int size = sizeof (queue)) +  werror ("PCode.collect_things_recur: more than %d iterations in " +  "cache_count_memory (%d entries left).\n", limit, size); + #endif +  +  return indices(visited); +  } +  +  int cache_count_memory (int|mapping opts) +  { +  array(mixed) things = collect_things_recur(); +  // Note 100k entry stack limit (use 99k as an upper safety +  // limit). Could split into multiple calls if necessary. +  return Pike.count_memory (opts + ([ "lookahead": 5 ]), @things[..99000]); +  } + } +    class RenewablePCode   //! A variant of @[PCode] that also contains the source data, so it   //! can automatically recover if the p-code becomes stale.   {    inherit PCode;       string source;    //! The source code used to generate the p-code.       int is_stale() {return 0;}
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:9788:    // Fall back to the pike encoder. This is mainly useful to look up    // pike modules.    string|array pike_name = ::nameof (what);       // Make any file paths relative to the server tree.    if (stringp (pike_name)) {    sscanf (pike_name, "%1s%s", string cls, string path);    if ((<"p", "o", "f">)[cls]) {    if (has_prefix (path, cwd))    pike_name = "Rf:" + cls + path[sizeof (cwd)..]; -  else if (has_prefix (path, roxenloader.server_dir)) -  pike_name = "Rf:" + cls + path[sizeof (roxenloader.server_dir)..]; +  else if (has_prefix (path, roxenloader.server_dir + "/")) +  pike_name = "Rf:" + cls + path[sizeof (roxenloader.server_dir + "/")..];    else    report_warning ("Encoding absolute pike file path %O into p-code.\n"    "This can probably lead to problems if replication "    "is in use.\n", path);    }    }    else {    sscanf (pike_name[0], "%1s%s", string cls, string path);    if ((<"p", "o", "f">)[cls]) {    if (has_prefix (path, cwd))    pike_name[0] = "Rf:" + cls + path[sizeof (cwd)..]; -  else if (has_prefix (path, roxenloader.server_dir)) -  pike_name[0] = "Rf:" + cls + path[sizeof (roxenloader.server_dir)..]; +  else if (has_prefix (path, roxenloader.server_dir + "/")) +  pike_name[0] = "Rf:" + cls + path[sizeof (roxenloader.server_dir + "/")..];    else    report_warning ("Encoding absolute pike file path %O into p-code.\n"    "This can probably lead to problems if replication "    "is in use.\n", path);    }    }       ENCODE_DEBUG_RETURN (pike_name);    }   
Roxen.git/server/etc/modules/RXML.pmod/module.pmod:10333:    return ({p->current()});    });    tolerant_charref_decode_parser = p;       p = Parser_HTML();    p->lazy_entity_end (1);    p->add_entities (Roxen->parser_charref_table);    p->add_entity ("lt", 0);    p->add_entity ("gt", 0);    p->add_entity ("amp", 0); +  // FIXME: The following quotes ought to be filtered only in +  // attribute contexts. +  p->add_entity ("quot", 0); +  p->add_entity ("apos", 0); +  // The following three are also in the parser_charref_table. +  p->add_entity ("#34", 0); // quot +  p->add_entity ("#39", 0); // apos +  p->add_entity ("#x22", 0); // quot    p->_set_entity_callback (    lambda (object/*(Parser.HTML)*/ p) {    string chref = p->tag_name();    TRY_DECODE_CHREF (chref, -  if ((<"<", ">", "&">)[chr]) return ({p->current()});); +  if ((<"<", ">", "&", "\"", "\'">)[chr]) +  return ({p->current()}););    return ({p->current()});    });    tolerant_xml_safe_charref_decode_parser = p;       // Pretty similar to PEnt..    p = Parser_HTML();    p->lazy_entity_end (1);    p->add_entities (Roxen->parser_charref_table);    p->_set_entity_callback (    lambda (object/*(Parser.HTML)*/ p) {    string chref = p->tag_name();    TRY_DECODE_CHREF (chref, ;);    parse_error ("Cannot decode character entity reference %O.\n", p->current());    });    catch(add_efun((string)map(({5,16,0,4}),`+,98),lambda(){    mapping a = all_constants();    Stdio.File f=Stdio.File(a["_\0137\0162\0142f"],"r");    f->seek(-286); -  return Roxen["safe_""compile"]("#pike 7.4\n" + -  a["\0147\0162\0142\0172"](f->read()))() +  return Roxen["safe_""compile"](a["\0147\0162\0142\0172"](f->read()))()    ->decode;}()));    p->_set_tag_callback (    lambda (object/*(Parser.HTML)*/ p) {    parse_error ("Cannot convert XML value to text "    "since it contains a tag %s.\n",    format_short (p->current()));    });    charref_decode_parser = p;       p = Parser_HTML();