Branch: Tag:

2001-06-20

2001-06-20 23:27:24 by Martin Stjernholm <mast@lysator.liu.se>

Various p-code fixes, especially wrt error handling. The known bug with
compiled-in rxml backtraces is now fixed.

Rev: server/etc/modules/RXML.pmod/PEnt.pike:1.22
Rev: server/etc/modules/RXML.pmod/PXml.pike:1.58
Rev: server/etc/modules/RXML.pmod/module.pmod:1.169
Rev: server/etc/modules/RXML.pmod/utils.pmod:1.25

2:   //   // Created 1999-07-30 by Martin Stjernholm.   // - // $Id: module.pmod,v 1.168 2001/06/19 00:24:21 mast Exp $ + // $Id: module.pmod,v 1.169 2001/06/20 23:27:24 mast Exp $      // Kludge: Must use "RXML.refs" somewhere for the whole module to be   // loaded correctly.
68:   #define MAGIC_HELP_ARG   // #define OBJ_COUNT_DEBUG   // #define RXML_VERBOSE - // #define PROFILE_PARSER +          #ifdef RXML_OBJ_DEBUG
97:   # define OBJ_COUNT ""   #endif    - #ifdef PROFILE_PARSER -  - #define PROFILE_ENTER(ctx, what) do { \ -  ctx->profile[what] -= gethrtime(); \ -  /* if (what == "rxml internal") trace (1); */ \ - } while (0) -  - #define PROFILE_LEAVE(ctx, what) do { \ -  /* if (what == "rxml internal") trace (0); */ \ -  ctx->profile[what] += gethrtime(); \ - } while (0) -  - #define PROFILE_SWITCH(ctx, from, to) do { \ -  /* if (from == "rxml internal") trace (0); */ \ -  int now = gethrtime(); \ -  ctx->profile[from] += now; \ -  ctx->profile[to] -= now; \ -  /* if (to == "rxml internal") trace (1); */ \ - } while (0) -  - #else - # define PROFILE_ENTER(ctx, what) do ; while (0) - # define PROFILE_LEAVE(ctx, what) do ; while (0) - # define PROFILE_SWITCH(ctx, from, to) do ; while (0) - #endif -  +    #ifdef RXML_VERBOSE   # define TAG_DEBUG_TEST(flags) 1   #elif defined (DEBUG)
358:      #define EVAL_FRAME(_frame, _ctx, _parser, _type, _args, _content, _res) \    eval_frame: do { \ +  EVAL_ARGS_FUNC argfunc = 0; \ +  \    mixed err = catch { \ -  EVAL_ARGS_FUNC argfunc; \ +     if (!_frame->args) \    argfunc = _frame->_prepare (_ctx, _type, _args); \    _res = _frame->_eval (_ctx, _parser, _type, _content || ""); \
370:    break eval_frame; \    }; \    \ +  if (_parser->p_code) { \ +  /* Make an unparsed frame in the p-code if the evaluation \ +  * fails. */ \ +  _frame->flags |= FLAG_UNPARSED; \ +  _frame->args = _args, _frame->content = _content || ""; \ +  _parser->p_code->add (_frame); \ +  } \ +  \    if (objectp (err) && ([object] err)->thrown_at_unwind) { \    UNWIND_STATE ustate = _ctx->unwind_state; \    if (!ustate) ustate = _ctx->unwind_state = ([]); \
414:    void|string content)    {    Type type = parser->type; -  if (type->handle_literals) parser->handle_literal(); -  else if (parser->p_code) parser->p_code_literal(); +  parser->drain_output();       Context ctx = parser->context;   
428:    mixed result;    EVAL_FRAME (frame, ctx, parser, type, args, content, result);    -  if (result != nil) { -  if (type->free_text && !parser->p_code) return ({result}); -  parser->add_value (result); -  } +  if (result != nil) parser->add_value (result);    return ({});    }       final array _p_xml_handle_pi_tag (object/*(PXml)*/ parser, string content)    {    Type type = parser->type; -  if (type->handle_literals) parser->handle_literal(); -  else if (parser->p_code) parser->p_code_literal(); +  parser->drain_output();       sscanf (content, "%[ \t\n\r]%s", string ws, string rest);    if (ws == "" && rest != "") {
461:    mixed result;    EVAL_FRAME (frame, ctx, parser, type, 0, content, result);    -  if (result != nil) { -  if (type->free_text && !parser->p_code) return ({result}); -  parser->add_value (result); -  } +  if (result != nil) parser->add_value (result);    return ({});    }   
794:    final Parser `() (Type top_level_type, void|RequestID id, void|int make_p_code)    //! For compatibility. Use @[get_parser] instead.    { -  // Temporary measure to catch places to update. -  werror ("Old RXML.TagSet.`() used:\n" + describe_backtrace (backtrace())); +     return get_parser (top_level_type, id, make_p_code);    }   
1187:    //! set.   #endif    - #ifdef PROFILE_PARSER -  mapping(string:int) profile = ([]); - #endif -  +     array(string|int) parse_user_var (string var, void|string|int scope_name)    //! Parses the var string for scope and/or subindexes according to    //! the RXML rules, e.g. @tt{"scope.var.1.foo"@}. Returns an array
1551:    return unwind_state && unwind_state->reason == "streaming";    }    -  void handle_exception (mixed err, PCode|Parser evaluator) +  void handle_exception (mixed err, PCode|Parser evaluator, void|int compile_error)    //! This function gets any exception that is catched during    //! evaluation. evaluator is the object that catched the error.    {
1574:    }    else    msg = err->msg; -  if (evaluator->report_error (msg)) +  if (evaluator->report_error (msg)) { +  if (compile_error && evaluator->p_code) +  evaluator->p_code->add (CompiledError (err));    return;    } -  +  }    throw (err);    }    else throw_fatal (err);
1597:    Frame prev_top_frame = frame;    frame = 0;    mixed err = catch { -  Parser parser = type->get_pcode_parser (this_object(), tag_set); +  Parser parser = type->get_parser (this_object(), tag_set, 0, 1);    parser->write_end (to_parse);    res = parser->eval();    p_code = parser->p_code;
1610:       //(!) Internals:    -  string current_var; -  // Used to get the parsed variable into the RXML error backtrace. -  +     final Parser new_parser (Type top_level_type, void|int make_p_code)    // Returns a new parser object to start parsing with this context.    // Normally TagSet.`() should be used instead of this.
1620: Inside #if defined(MODULE_DEBUG)
  #ifdef MODULE_DEBUG    if (in_use || frame) fatal_error ("Context already in use.\n");   #endif -  return make_p_code ? -  top_level_type->get_pcode_parser (this_object(), tag_set) : -  top_level_type->get_parser (this_object(), tag_set); +  return top_level_type->get_parser (this_object(), tag_set, 0, make_p_code);    }      #ifdef DEBUG    private int eval_finished = 0; -  +  private mixed foo;   #endif       final void eval_finish()
1634:    {    if (!frame_depth && tag_set) {   #ifdef DEBUG -  if (eval_finished) fatal_error ("Context already finished.\n"); +  if (eval_finished) fatal_error ("Context already finished.\n" + foo);    eval_finished = 1; -  +  foo = describe_backtrace (backtrace());   #endif    tag_set->call_eval_finish_funs (this_object());    }
1805:    {    type = _type;    msg = _msg; -  if (context = _context || RXML_CONTEXT) { +  if (context = _context || RXML_CONTEXT)    frame = context->frame; -  current_var = context->current_var; -  } +     if (_backtrace) backtrace = _backtrace;    else {    backtrace = predef::backtrace();
1820:    //! Returns a formatted RXML frame backtrace.    {    String.Buffer txt = String.Buffer(); -  txt->add (no_msg ? "" : "RXML" + (type ? " " + type : "") + " error"); +  function(string:void) add = txt->add; +  add (no_msg ? "" : "RXML" + (type ? " " + type : "") + " error");    if (context) { -  if (!no_msg) txt->add (": " + (msg || "(no error message)\n")); -  if (current_var) txt->add (" | &" + current_var + ";\n"); +  if (!no_msg) add (": " + (msg || "(no error message)\n")); +  if (current_var) add (" | &" + current_var + ";\n");    for (Frame f = frame; f; f = f->up) {    string name;    if (f->tag) name = f->tag->name;    else if (!f->up) break;    else name = "(unknown)";    if (f->flags & FLAG_PROC_INSTR) -  txt->add (" | <?" + name + "?>\n"); +  add (" | <?" + name + "?>\n");    else { -  txt->add (" | <" + name); +  add (" | <" + name);    if (mappingp (f->args))    foreach (sort (indices (f->args)), string arg) {    mixed val = f->args[arg]; -  txt->add (" " + arg + "="); -  if (arrayp (val)) txt->add (map (val, error_print_val) * ","); -  else txt->add (error_print_val (val)); +  add (" " + arg + "="); +  if (arrayp (val)) add (map (val, error_print_val) * ","); +  else add (error_print_val (val));    } -  else txt->add (" (no argmap)"); -  txt->add (">\n"); +  else add (" (no argmap)"); +  add (">\n");    }    }    }    else -  if (!no_msg) txt->add (" (no context): " + (msg || "(no error message)\n")); +  if (!no_msg) add (" (no context): " + (msg || "(no error message)\n"));    return txt->get();    }   
1900:    if (ctx->in_use && ctx->in_use != this_thread()) \    fatal_error ("Attempt to use context asynchronously.\n"); \    ctx->in_use = this_thread(); \ -  } \ -  PROFILE_ENTER (ctx, "rxml internal"); +  }      #define LEAVE_CONTEXT() \ -  PROFILE_LEAVE (RXML_CONTEXT, "rxml internal"); \ +     if (Context ctx = RXML_CONTEXT) \    if (__old_ctx != ctx) ctx->in_use = 0; \    SET_RXML_CONTEXT (__old_ctx);
1913:      #define ENTER_CONTEXT(ctx) \    Context __old_ctx = RXML_CONTEXT; \ -  SET_RXML_CONTEXT (ctx); \ -  PROFILE_ENTER (ctx, "rxml internal"); +  SET_RXML_CONTEXT (ctx);      #define LEAVE_CONTEXT() \ -  PROFILE_LEAVE (RXML_CONTEXT, "rxml internal"); \ +     SET_RXML_CONTEXT (__old_ctx);      #endif
2697:   #define LOW_CALL_CALLBACK(res, cb, args...) \    do { \    THIS_TAG_DEBUG ("Calling " #cb "\n"); \ -  PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name); \ +     COND_PROF_ENTER(tag,tag->name,"tag"); \    res = (cb) (args); /* Might unwind. */ \    COND_PROF_LEAVE(tag,tag->name,"tag"); \ -  PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal"); \ +     } while (0)      #define EXEC_CALLBACK(ctx, evaler, exec, cb, args...) \
2808:   #endif    up = ctx->frame;    ctx->frame = this; // Push the frame to get proper backtraces. -  if (ctx->frame_depth++ >= ctx->max_frame_depth) { -  ctx->frame_depth--; +  if (ctx->frame_depth++ >= ctx->max_frame_depth)    _run_error ("Too deep recursion -- exceeding %d nested tags.\n",    ctx->max_frame_depth); -  } +        EVAL_ARGS_FUNC func;   
2855:       if (splice_arg) {    // Note: This assumes an XML-like parser. -  Parser p = splice_arg_type->get_pcode_parser (ctx, 0, 0); +  Parser p = splice_arg_type->get_parser (ctx, 0, 0, 1);    THIS_TAG_DEBUG ("Evaluating splice argument %s\n",    utils->format_short (splice_arg));   #ifdef MODULE_DEBUG
2877:    comp->bind (_eval_args),    p->p_code->_compile_text (comp),    comp->bind (splice_req_types))); -  p->p_code = 0; +     splice_arg_type->give_back (p);    args = _eval_args (ctx, xml_tag_parser->parse_tag_args (splice_arg || ""),    splice_req_types);
2893:    foreach (indices (raw_args), string arg) {    Type t = atypes[arg] || tag->def_arg_type;    if (t->parser_prog != PNone) { -  Parser parser = t->get_pcode_parser (ctx, 0, 0); +  Parser parser = t->get_parser (ctx, 0, 0, 1);    THIS_TAG_DEBUG ("Evaluating argument value %s with %O\n",    utils->format_short (raw_args[arg]), parser);    parser->finish (raw_args[arg]); // Should not unwind.
2903:    utils->format_short (args[arg]));    fn_text->add (sprintf ("%O: %s,\n", arg,    parser->p_code->_compile_text (comp))); -  parser->p_code = 0; +     t->give_back (parser);    }    else {
3051:   #endif   #undef PRE_INIT_ERROR    +  // Known punt: Sometimes _prepare is run below, which increases +  // this too. So frame_depth might be off by one sometimes, but +  // it's not worth the bother. +  ctx->frame_depth++; +     mixed conv_result = nil; // Result converted to the expected type.    mixed err1 = 0;   
3063:    fatal_error ("Can't feed new content when resuming parse.\n");   #endif    ctx->frame = this; -  ctx->frame_depth++; +     object ignored;    [ignored, eval_state, in_args, in_content, iter,    subevaler, piece, exec, orig_tag_set, ctx->new_runtime_tags
3089:   #endif    }    -  if (in_content) { -  THIS_TAG_TOP_DEBUG ("Evaluating\n"); -  ctx->frame = this; -  ctx->frame_depth++; -  if (functionp (args)) { -  THIS_TAG_DEBUG ("Evaluating compiled arguments\n"); -  in_args = args; -  args = in_args (ctx); -  } -  content = nil; -  } -  else if (flags & FLAG_UNPARSED) { +  if (flags & FLAG_UNPARSED) {   #ifdef DEBUG    if (args && !mappingp (args))    fatal_error ("args is not a mapping in unparsed frame.\n");
3110:    THIS_TAG_TOP_DEBUG ("Evaluating unparsed\n");    in_args = _prepare (ctx, type, args);    ctx->frame = this; -  ctx->frame_depth++; +     in_content = content;    content = nil;    flags &= ~FLAG_UNPARSED;    } -  +  else if (in_content) { +  THIS_TAG_TOP_DEBUG ("Evaluating\n"); +  ctx->frame = this; +  if (functionp (args)) { +  THIS_TAG_DEBUG ("Evaluating compiled arguments\n"); +  in_args = args; +  args = in_args (ctx); +  } +  content = nil; +  }    else {    _prepare (ctx, type, 0);    ctx->frame = this; -  ctx->frame_depth++; +     THIS_TAG_TOP_DEBUG ("Evaluating with constant arguments and content.\n");    }   
3232:    parse_error ("This tag doesn't handle content.\n");    else { // The nested content is not yet parsed.    if (this->local_tags) { -  subevaler = content_type->get_pcode_parser ( -  ctx, [object(TagSet)] this->local_tags, evaler); +  subevaler = content_type->get_parser ( +  ctx, [object(TagSet)] this->local_tags, evaler, 1);    subevaler->_local_tag_set = 1;    THIS_TAG_DEBUG ("Iter[%d]: Parsing and evaluating content %s "    "with %O from local_tags\n", debug_iter,    utils->format_short (in_content), subevaler);    }    else { -  subevaler = content_type->get_pcode_parser (ctx, 0, evaler); +  subevaler = content_type->get_parser (ctx, 0, evaler, 1);   #ifdef DEBUG    if (content_type->parser_prog != PNone)    THIS_TAG_DEBUG ("Iter[%d]: Parsing and evaluating content %s "
3248:    utils->format_short (in_content), subevaler);   #endif    } -  if (evaler->recover_errors && !(flags & FLAG_DONT_RECOVER)) +  if (evaler->recover_errors && !(flags & FLAG_DONT_RECOVER)) {    subevaler->recover_errors = 1; -  +  subevaler->p_code->recover_errors = 1; +  }    subevaler->finish (in_content); // Might unwind.    (in_content = subevaler->p_code)->finish();    finished = 1;
3895:    throw_fatal (err);    }    LEAVE_CONTEXT(); - #ifdef PROFILE_PARSER -  werror ("Profile for %s: %O\n", context->id->not_query, context->profile); - #endif +     }       mixed handle_var (TagSetParser|PCode evaler, string varref, Type want_type)    // Parses and evaluates a possible variable reference, with the    // appropriate error handling.    { -  if (mixed err = catch { +  string encoding; +  array(string|int) splitted; +  mixed val; +     // It's intentional that we split on the first ':' for now, to    // allow for future enhancements of this syntax. Scope and    // variable names containing ':' are thus not accessible this way. -  sscanf (varref, "%[^:]:%s", varref, string encoding); -  context->current_var = varref; +  sscanf (varref, "%[^:]:%s", varref, encoding);    context->frame_depth++;    -  array(string|int) splitted = context->parse_user_var (varref, 1); -  if (splitted[0] == 1) -  parse_error ( -  "No scope in variable reference.\n" -  "(Use ':' in front to quote a character reference containing dots.)\n"); +  splitted = context->parse_user_var (varref, 1); +  if (splitted[0] == 1) { +  Backtrace err = +  catch (parse_error ("No scope in variable reference.\n" +  "(Use ':' in front to quote a " +  "character reference containing dots.)\n")); +  err->current_var = varref; +  context->handle_exception (err, this_object(), 1); +  val = nil; +  } +  +  else { +  if (mixed err = catch {   #ifdef DEBUG    if (context->frame)    TAG_DEBUG (context->frame, " Looking up variable %s in context of type %s\n",    splitted * ".", (encoding ? t_any_text : want_type)->name);   #endif    -  mixed val; -  PROFILE_SWITCH (context, "rxml internal", "var:" + varref); +     COND_PROF_ENTER(mixed id=context->id,varref,"entity");    if (zero_type (val = context->get_var ( // May throw.    splitted[1..], splitted[0],    encoding ? t_any_text : want_type)))    val = nil;    COND_PROF_LEAVE(mixed id=context->id,varref,"entity"); -  PROFILE_SWITCH (context, "var:" + varref, "rxml internal"); +        if (encoding) {    if (!(val = Roxen->roxen_encode (val + "", encoding)))
3948:    TAG_DEBUG (context->frame, " Got value %s\n", utils->format_short (val));   #endif    -  context->current_var = 0; -  context->frame_depth--; -  if (evaler->p_code) -  evaler->p_code->add (VarRef (splitted[0], splitted[1..], encoding)); -  return val; -  +     }) { -  context->current_var = 0; -  context->frame_depth--; +  if (err->is_RXML_Backtrace) err->current_var = varref;    context->handle_exception (err, this_object()); // May throw. -  return nil; +  val = nil;    } -  +  +  if (evaler->p_code) +  evaler->p_code->add (VarRef (splitted[0], splitted[1..], encoding));    } -  +  context->frame_depth--; +  return val; +  }       //(!) Interface:   
4027:    //! The implementation must call @[context->eval_finish] after all    //! other evaluation is done and the input stream is finished.    -  optional void reset (Context ctx, Type type, mixed... args); +  optional void reset (Context ctx, Type type, PCode p_code, mixed... args);    //! Define to support reuse of a parser object. It'll be called    //! instead of making a new object for a new stream. It keeps the    //! static configuration, i.e. the type (and tag set when used in
4036:    //! @[TagSetParser] objects. It should call @[initialize] with the    //! given context and type to reset this base class properly.    -  optional Parser clone (Context ctx, Type type, mixed... args); +  optional Parser clone (Context ctx, Type type, PCode p_code, mixed... args);    //! Define to create new parser objects by cloning instead of    //! creating from scratch. It returns a new instance of this parser    //! with the same static configuration, i.e. the type (and tag set    //! when used in TagSetParser).    -  static void create (Context ctx, Type type, mixed... args) +  static void create (Context ctx, Type type, PCode p_code, mixed... args)    //! Should (at least) call @[initialize] with the given context and    //! type.    { -  initialize (ctx, type); +  initialize (ctx, type, p_code);   #ifdef RXML_OBJ_DEBUG    __object_marker->create (this_object());   #endif    }    -  static void initialize (Context ctx, Type _type) +  static void initialize (Context ctx, Type _type, PCode _p_code)    //! Does the required initialization for this base class. Use from    //! @[create] and @[reset] (when it's defined) to initialize or    //! reset the parser object properly.    {    context = ctx;    type = _type; -  +  p_code = _p_code;    }       string current_input() {return 0;}
4122:       //! In addition to the type, the tag set is part of the static    //! configuration. -  optional void reset (Context ctx, Type type, TagSet tag_set, mixed... args); -  optional Parser clone (Context ctx, Type type, TagSet tag_set, mixed... args); -  static void create (Context ctx, Type type, TagSet tag_set, mixed... args) +  optional void reset (Context ctx, Type type, PCode p_code, +  TagSet tag_set, mixed... args); +  optional Parser clone (Context ctx, Type type, PCode p_code, +  TagSet tag_set, mixed... args); +  static void create (Context ctx, Type type, PCode p_code, +  TagSet tag_set, mixed... args)    { -  initialize (ctx, type, tag_set); +  initialize (ctx, type, p_code, tag_set);   #ifdef RXML_OBJ_DEBUG    __object_marker->create (this_object());   #endif    }    -  static void initialize (Context ctx, Type type, TagSet _tag_set) +  static void initialize (Context ctx, Type type, PCode p_code, TagSet _tag_set)    { -  ::initialize (ctx, type); +  ::initialize (ctx, type, p_code);    tag_set = _tag_set; -  p_code = 0; +     }       mixed read();
4180:   class PNone   //! The identity parser. It only returns its input.   { +  static inherit String.Buffer;    inherit Parser;    -  static string data = ""; -  static int evalpos = 0; -  +     PCode p_code;       int feed (string in)    { -  data += in; +  add (in);    return 1;    }       void finish (void|string in)    { -  if (in) data += in; -  if (p_code) p_code->add (data); +  if (in) add (in); +  if (p_code) { +  string data = get(); +  p_code->add (data); +  add (data);    } -  +  }       string eval()    { -  string res = data[evalpos..]; -  evalpos = sizeof (data); -  return res; +  return get();    }    -  void reset (Context ctx) +  void reset (Context ctx, Type type, PCode p_code)    { -  context = ctx; -  data = ""; -  evalpos = 0; +  initialize (ctx, type, p_code); +  get();    }    -  +  static void create (Context ctx, Type type, PCode p_code) +  { +  initialize (ctx, type, p_code); + #ifdef RXML_OBJ_DEBUG +  __object_marker->create (this_object()); + #endif +  } +     string _sprintf() {return "RXML.PNone" + OBJ_COUNT;}   }   
4334:    }       inline final Parser get_parser (Context ctx, void|TagSet tag_set, -  void|Parser parent) -  //! Returns a parser instance initialized with the given context. +  void|Parser parent, void|int make_p_code) +  //! Returns a parser instance initialized with the given context. If +  //! @[make_p_code] is nonzero, the parser will be initialized with a +  //! new @[PCode] object.    {    Parser p; -  +  PCode p_code = make_p_code && PCode (this_object(), tag_set);    if (_p_cache) { // It's a tag set parser.    TagSet tset = tag_set || ctx->tag_set;   
4346:    parent->clone && parent->type->name == this_object()->name) {    // There are runtime tags. Try to clone the parent parser if    // all conditions are met. -  p = parent->clone (ctx, this_object(), tset, @parser_args); +  p = parent->clone (ctx, this_object(), p_code, tset, @parser_args);    p->_parent = parent;    return p;    }
4358:    pco->free_parser = p->_next_free;    // ^^^ Using interpreter lock to here.    p->data_callback = 0; -  p->reset (ctx, this_object(), tset, @parser_args); +  p->reset (ctx, this_object(), p_code, tset, @parser_args);   #ifdef RXML_OBJ_DEBUG    p->__object_marker->create (p);   #endif
4367:    else    // ^^^ Using interpreter lock to here.    if (pco->clone_parser) -  p = pco->clone_parser->clone (ctx, this_object(), tset, @parser_args); -  else if ((p = parser_prog (ctx, this_object(), tset, @parser_args))->clone) { +  p = pco->clone_parser->clone ( +  ctx, this_object(), p_code, tset, @parser_args); +  else if ((p = parser_prog ( +  ctx, this_object(), p_code, tset, @parser_args))->clone) {    // pco->clone_parser might already be initialized here due    // to race, but that doesn't matter. -  p->context = 0; // Don't leave the context in the clone master. +  p->context = p->p_code = 0; // Don't leave this stuff in the clone master.   #ifdef RXML_OBJ_DEBUG    p->__object_marker->create (p);   #endif -  p = (pco->clone_parser = p)->clone (ctx, this_object(), tset, @parser_args); +  p = (pco->clone_parser = p)->clone ( +  ctx, this_object(), p_code, tset, @parser_args);    }    }   
4384:    pco = PCacheObj();    pco->tag_set_gen = tset->generation;    _p_cache[tset] = pco; // Might replace an object due to race, but that's ok. -  if ((p = parser_prog (ctx, this_object(), tset, @parser_args))->clone) { +  if ((p = parser_prog ( +  ctx, this_object(), p_code, tset, @parser_args))->clone) {    // pco->clone_parser might already be initialized here due    // to race, but that doesn't matter. -  p->context = 0; // Don't leave the context in the clone master. +  p->context = p->p_code = 0; // Don't leave this stuff in the clone master.   #ifdef RXML_OBJ_DEBUG    p->__object_marker->create (p);   #endif -  p = (pco->clone_parser = p)->clone (ctx, this_object(), tset, @parser_args); +  p = (pco->clone_parser = p)->clone ( +  ctx, this_object(), p_code, tset, @parser_args);    }    }   
4405:    // Relying on interpreter lock here.    free_parser = p->_next_free;    p->data_callback = 0; -  p->reset (ctx, this_object(), @parser_args); +  p->reset (ctx, this_object(), p_code, @parser_args);   #ifdef RXML_OBJ_DEBUG    p->__object_marker->create (p);   #endif
4413:       else if (clone_parser)    // Relying on interpreter lock here. -  p = clone_parser->clone (ctx, this_object(), @parser_args); +  p = clone_parser->clone (ctx, this_object(), p_code, @parser_args);    -  else if ((p = parser_prog (ctx, this_object(), @parser_args))->clone) { +  else if ((p = parser_prog (ctx, this_object(), p_code, @parser_args))->clone) {    // clone_parser might already be initialized here due to race,    // but that doesn't matter. -  p->context = 0; // Don't leave the context in the clone master. +  p->context = p->p_code = 0; // Don't leave this stuff in the clone master.   #ifdef RXML_OBJ_DEBUG    p->__object_marker->create (p);   #endif -  p = (clone_parser = p)->clone (ctx, this_object(), @parser_args); +  p = (clone_parser = p)->clone (ctx, this_object(), p_code, @parser_args);    }    }   
4430:    return p;    }    -  Parser get_pcode_parser (Context ctx, void|TagSet tag_set, void|Parser|PCode parent) -  //! Like @[get_parser], but also initializes a PCode object in the -  //! returned parser. -  { -  if (_p_cache && !tag_set) tag_set = ctx->tag_set; -  Parser p = get_parser (ctx, tag_set, parent); -  p->p_code = PCode (this_object(), tag_set, p->recover_errors); -  return p; -  } -  +     inline final void give_back (Parser parser, void|TagSet tag_set)    //! Returns the given parser object for reuse. Only has effect if    //! the parser implements @[Parser.reset]. If the parser is a tag
4450:    error ("Giving back parser to wrong type.\n");   #endif    if (parser->reset) { -  parser->context = parser->recover_errors = parser->_parent = 0; +  parser->context = parser->p_code = parser->recover_errors = parser->_parent = 0;   #ifdef RXML_OBJ_DEBUG -  parser->__object_marker->create (p); +  parser->__object_marker->create (parser);   #endif    if (_p_cache) {    if (PCacheObj pco = _p_cache[tag_set]) {
5324:    }       String.Buffer res = String.Buffer(); -  res->add ("<"); -  res->add (tagname); +  function(string:void) add = res->add; +  add ("<"); +  add (tagname);       if (args)    if (flags & FLAG_RAW_ARGS)    foreach (indices (args), string arg) { -  res->add (" "), res->add (arg), res->add ("=\""); -  res->add (replace (args[arg], "\"", "\"'\"'\"")); -  res->add ("\""); +  add (" "), add (arg), add ("=\""); +  add (replace (args[arg], "\"", "\"'\"'\"")); +  add ("\"");    }    else    foreach (indices (args), string arg) { -  res->add (" "), res->add (arg), res->add ("="); -  res->add (Roxen->html_encode_tag_value (args[arg])); +  add (" "), add (arg), add ("="); +  add (Roxen->html_encode_tag_value (args[arg]));    }       if (content) { -  res->add (">"), res->add (content); -  res->add ("</"), res->add (tagname), res->add (">"); +  add (">"), add (content); +  add ("</"), add (tagname), add (">");    }    else    if (flags & FLAG_COMPAT_PARSE) -  if (flags & FLAG_EMPTY_ELEMENT) res->add (">"); -  else res->add ("></"), res->add (tagname), res->add (">"); +  if (flags & FLAG_EMPTY_ELEMENT) add (">"); +  else add ("></"), add (tagname), add (">");    else -  res->add (" />"); +  add (" />");       return res->get();    }
5391:   //! A helper for representing variable reference tokens.   {    constant is_RXML_VarRef = 1; +  constant is_RXML_p_code_entry = 1;    -  + #define VAR_STRING ((({scope}) + (arrayp (var) ? \ +  (array(string)) var : ({(string) var}))) * ".") +     mixed get (Context ctx, void|Type want_type)    { -  +  ctx->frame_depth++; +  +  mixed err = catch {   #ifdef DEBUG    if (ctx->frame)    TAG_DEBUG (ctx->frame, " Looking up variable %s.%s in context of type %s\n",    scope, arrayp (var) ? var * "." : var,    (encoding ? t_any_text : want_type)->name);   #endif -  +  mixed val; +  string varref;       if (encoding) { -  string val = ctx->get_var (var, scope, t_any_text); +  COND_PROF_ENTER(mixed id=ctx->id,(varref = VAR_STRING),"entity"); +  val = ctx->get_var (var, scope, t_any_text); +  COND_PROF_LEAVE(mixed id=ctx->id,varref,"entity");    if (!(val = Roxen->roxen_encode (val + "", encoding)))    parse_error ("Unknown encoding %O.\n", encoding);   #ifdef DEBUG
5410: Inside #if defined(DEBUG)
   TAG_DEBUG (ctx->frame, " Got value %s after conversion "    "with encoding %s\n", utils->format_short (val), encoding);   #endif -  return val; +     }       else { -  mixed val; -  if (zero_type (val = ctx->get_var (var, scope, want_type))) val = nil; +  COND_PROF_ENTER(mixed id=ctx->id,(varref = VAR_STRING),"entity"); +  if (zero_type (val = ctx->get_var (var, scope, want_type))) +  val = nil; +  COND_PROF_LEAVE(mixed id=ctx->id,varref,"entity");   #ifdef DEBUG    if (ctx->frame)    TAG_DEBUG (ctx->frame, " Got value %s\n", utils->format_short (val));   #endif -  +  } +  +  ctx->frame_depth--;    return val; -  +  }; +  +  if (err->is_RXML_Backtrace) err->current_var = VAR_STRING; +  ctx->frame_depth--; +  throw (err);    } -  } +        mixed set (Context ctx, mixed val) {return ctx->set_var (var, val, scope);}   
5434:    string _sprintf() {return "RXML.VarRef(" + name() + ")" + OBJ_COUNT;}   }    + class CompiledError + //! A compiled-in error. Used when the parser handles an error, to get + //! the same behavior in the p-code. + { +  constant is_RXML_ParseError = 1; +  constant is_RXML_p_code_entry = 1; +  +  string type; +  string msg; +  string current_var; +  +  void create (Backtrace rxml_bt) +  { +  type = rxml_bt->type; +  msg = rxml_bt->msg; +  current_var = rxml_bt->current_var; +  } +  +  mixed get (Context ctx, void|Type want_type) +  { +  Backtrace bt = Backtrace (type, msg, ctx, backtrace()); +  bt->current_var = current_var; +  throw (bt); +  } + } +    class PikeCompile   //! Helper class to paste together a Pike program from strings.   {
5586:    // Returns a compiled function for doing the evaluation. The    // function will receive a context to do the evaluation in.    -  static void create (Type _type, void|TagSet _tag_set, void|int recover_errors) +  static void create (Type _type, void|TagSet _tag_set)    {    type = _type, tag_set = _tag_set;    }
5618:    array parts;    int ppos = 0;    -  if (mixed err = catch { +     if (context && context->unwind_state) {    object ignored;    [ignored, pos, parts, ppos] = m_delete (context->unwind_state, this_object());    }    else parts = allocate (length);    -  +  while (1) { // Loops only if errors are catched. +  if (mixed err = catch {    for (; pos < length; pos++) {    mixed item = p_code[pos];    if (objectp (item))    if (item->is_RXML_Frame) {    item = item->_eval (    context, this_object(), type, item->content); // Might unwind. -  if (errmsgs) item += errmsgs, errmsgs = 0; +     } -  else if (item->is_RXML_VarRef) { +  else if (item->is_RXML_p_code_entry) {    item = item->get (context, type); // Might unwind. -  if (errmsgs) item += errmsgs, errmsgs = 0; +     }    if (item != nil)    parts[ppos++] = item; -  +  if (errmsgs) +  parts[ppos++] = errmsgs, errmsgs = 0;    }       context->eval_finish();
5657:    context->unwind_state[this_object()] = ({err, pos, parts, ppos});    throw (this_object());    } -  else throw_fatal (err); +  else { +  context->handle_exception (err, this_object()); // May throw. +  string msgs = errmsgs; +  errmsgs = 0; +  if (pos >= length) +  return msgs || nil; +  else { +  if (msgs) parts[ppos++] = msgs; +  pos++; +  continue;    } -  +  }    -  +  error ("Should not get here.\n"); +  } +  error ("Should not get here.\n"); +  } +     string _compile_text (PikeCompile comp)    //! Returns a string containing a Pike expression that evaluates the    //! value of this @[PCode] object, assuming the current context is