Branch: Tag:

2002-10-02

2002-10-02 23:10:26 by Martin Stjernholm <mast@lysator.liu.se>

More controlled reuse of PikeCompile objects (it's not good to store
them in the context since several p-code objects with very different
life lengths can be created/updated in the same context). Also a fix
in the delayed resolve of arg functions (a frame that gets stored in
result p-code and that got FLAG_DONT_CACHE_RESULT set from inner tags
won't have a delayed resolved arg function at the time it's added, but
might get it later on). These two things fixes [bug 3235 (#3235)], which was
introduced in the previous revision (thus it's 3.3 only).

Also added some more debug checks in PikeCompile.

Rev: server/etc/modules/RXML.pmod/module.pmod:1.292

2:   //   // Created 1999-07-30 by Martin Stjernholm.   // - // $Id: module.pmod,v 1.291 2002/09/03 16:36:46 mast Exp $ + // $Id: module.pmod,v 1.292 2002/10/02 23:10:26 mast Exp $      // Kludge: Must use "RXML.refs" somewhere for the whole module to be   // loaded correctly.
2151:    eval_finished = 1;   #endif    if (tag_set) tag_set->call_eval_finish_funs (this_object()); -  if (p_code_comp) p_code_comp->compile(); // Fix all delayed resolves. +     }    }   
2238:    // Nonzero if the persistent state of the evaluated rxml has    // changed. Never negative.    -  PikeCompile p_code_comp; -  // The PikeCompile object for collecting any produced Pike byte code -  // during p-code compilation. -  +     PCode evaled_p_code;    // The p-code object of the innermost frame that collects evaled    // content (i.e. got FLAG_GET_EVALED_CONTENT set).
3630:       EVAL_ARGS_FUNC|string _prepare (Context ctx, Type type,    mapping(string:string) raw_args, -  int make_p_code) +  PikeCompile comp)    // Evaluates raw_args simultaneously as generating the    // EVAL_ARGS_FUNC function. The result of the evaluations is stored    // in args. Might be destructive on raw_args. No evaluation of
3685:    String.Buffer fn_text;    function(string...:void) fn_text_add;    PCode sub_p_code = 0; -  PikeCompile comp; -  if (make_p_code) { +  if (comp) {    fn_text_add = (fn_text = String.Buffer())->add;    fn_text_add ("mixed tmp;\n");    sub_p_code = PCode (0, 0); -  comp = ctx->p_code_comp; +     }       if (splice_arg) {    // Note: This assumes an XML-like parser. -  if (make_p_code) sub_p_code->create (splice_arg_type, ctx, ctx->tag_set); +  if (comp) +  sub_p_code->create (splice_arg_type, ctx, ctx->tag_set, 0, comp);    Parser parser = splice_arg_type->get_parser (ctx, ctx->tag_set, 0,    sub_p_code);    THIS_TAG_DEBUG ("Evaluating splice argument %s\n",
3713: Inside #if defined(MODULE_DEBUG)
   throw_fatal (err);    }   #endif -  if (make_p_code) +  if (comp)    if (tag)    fn_text_add (    "return ", comp->bind (tag->_eval_splice_args), "(ctx,",
3734:    }    else {    args = raw_args; -  if (make_p_code) fn_text_add ("return ([\n"); +  if (comp) fn_text_add ("return ([\n");    }      #ifdef MODULE_DEBUG
3742:   #endif    TagSet ctx_tag_set = ctx->tag_set;    Type default_type = tag ? tag->def_arg_type : t_any_text (PNone); -  if (make_p_code) +  if (comp)    foreach (indices (raw_args), string arg) {    Type t = atypes[arg] || default_type;    if (t->parser_prog != PNone) { -  sub_p_code->create (t, ctx, ctx_tag_set); +  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",
3788:    }   #endif    -  if (make_p_code) { +  if (comp) {    fn_text_add ("]);\n");    func = comp->add_func (    "mapping(string:mixed)", "object ctx, object evaler", fn_text->get());
3865:    // since it handles unwinding and rewinding.    {    RequestID id = ctx->id; +  PikeCompile comp;       // Unwind state data:   #define EVSTAT_NONE 0
3975: Inside #if defined(DEBUG)
   if (content && !stringp (content))    fatal_error ("content is not a string in unparsed frame: %O.\n", content);   #endif -  THIS_TAG_TOP_DEBUG ("Evaluating%s unparsed\n", -  ctx->make_p_code ? " and compiling" : ""); +     if (ctx->make_p_code) { -  if (!ctx->p_code_comp) ctx->p_code_comp = PikeCompile(); -  in_args = _prepare (ctx, type, args && args + ([]), 1); +  if (evaler->is_RXML_PCode) { +  if (!(comp = evaler->p_code_comp)) { +  comp = evaler->p_code_comp = PikeCompile(); +  THIS_TAG_TOP_DEBUG ("Evaluating and compiling unparsed" +  " (with new %O in %O)\n", comp, evaler); +  } +  else +  THIS_TAG_TOP_DEBUG ("Evaluating and compiling unparsed" +  " (with old %O in %O)\n", comp, evaler); +  } +  else { +  if (!evaler->p_code) { + #ifdef DEBUG +  fatal_error ("Frame is being compiled but evaler %O " +  "doesn't have a PCode object\n", evaler); + #endif +  // Handle this gracefully if we're not using debug since this +  // situation can occur easily by mistake if a context is used +  // recursively. +  comp = PikeCompile(); +  THIS_TAG_TOP_DEBUG ("Evaluating and compiling unparsed" +  " (with new temporary %O)\n", comp); +  } +  else +  if (!(comp = evaler->p_code->p_code_comp)) { +  comp = evaler->p_code->p_code_comp = PikeCompile(); +  THIS_TAG_TOP_DEBUG ("Evaluating and compiling unparsed" +  " (with new %O in %O in %O)\n", +  comp, evaler->p_code, evaler); +  } +  else +  THIS_TAG_TOP_DEBUG ("Evaluating and compiling unparsed" +  " (with old %O in %O in %O)\n", +  comp, evaler->p_code, evaler); +  } +  in_args = _prepare (ctx, type, args && args + ([]), comp);    PCODE_UPDATE_MSG ("%O: P-code update since args has been compiled.\n",    this_object());    ctx->state_updated++;    } -  else +  else { +  THIS_TAG_TOP_DEBUG ("Evaluating unparsed\n");    _prepare (ctx, type, args && args + ([]), 0); -  +  }    in_content = content;    if (!in_content || in_content == "") flags |= FLAG_MAY_CACHE_RESULT;    }
4112:       else if (!in_content || in_content == "") {    if (flags & FLAG_GET_EVALED_CONTENT) { -  this_object()->evaled_content = PCode (content_type, ctx); +  this_object()->evaled_content = +  PCode (content_type, ctx, 0, 0, +  evaler->p_code_comp || +  evaler->p_code && evaler->p_code->p_code_comp);    this_object()->evaled_content->finish();    }    break eval_content; // No content to handle.
4139:    ctx->make_p_code = 1;    if (TagSet local_tags =    [object(TagSet)] this_object()->local_tags) { -  PCode p_code = unevaled_content = -  ctx->make_p_code && PCode (content_type, ctx, local_tags); -  +     if (flags & FLAG_GET_EVALED_CONTENT) -  +  // Do not pass on a PikeCompile object here since +  // the result p-code will typically have a +  // different lifespan than the content p-code.    this_object()->evaled_content = ctx->evaled_p_code =    PCode (content_type, ctx, local_tags, 1); -  +  +  PCode p_code = unevaled_content = +  ctx->make_p_code && +  PCode (content_type, ctx, local_tags, 0, +  ctx->evaled_p_code ? ctx->evaled_p_code->p_code_comp : +  evaler->p_code_comp || +  evaler->p_code && evaler->p_code->p_code_comp); +  // Must use the same PikeCompile object for both +  // content and result collection since +  // FLAG_DONT_CACHE_RESULT frames in the result p-code +  // will resolve to the same compiled args function. +     if (PCode evaled_p_code = ctx->evaled_p_code)    if (p_code) p_code->p_code = evaled_p_code;    else p_code = evaled_p_code;
4162:    }       else { -  PCode p_code = unevaled_content = -  ctx->make_p_code && PCode (content_type, ctx, ctx->tag_set); -  +     if (flags & FLAG_GET_EVALED_CONTENT) -  +  // Do not pass on a PikeCompile object here since +  // the result p-code will typically have a +  // different lifespan than the content p-code.    this_object()->evaled_content = ctx->evaled_p_code =    PCode (content_type, ctx, ctx->tag_set, 1); -  +  +  PCode p_code = unevaled_content = +  ctx->make_p_code && +  PCode (content_type, ctx, ctx->tag_set, 0, +  ctx->evaled_p_code ? ctx->evaled_p_code->p_code_comp : +  evaler->p_code_comp || +  evaler->p_code && evaler->p_code->p_code_comp); +  // Must use the same PikeCompile object for both +  // content and result collection since +  // FLAG_DONT_CACHE_RESULT frames in the result p-code +  // will resolve to the same compiled args function. +     if (PCode evaled_p_code = ctx->evaled_p_code)    if (p_code) p_code->p_code = evaled_p_code;    else p_code = evaled_p_code;
4200:       else {    subevaler = in_content; + #ifdef DEBUG +  if (!evaler->is_RXML_PCode) +  fatal_error ("Expected the evaler %O to " +  "be a PCode object here.\n", evaler); + #endif       if (flags & FLAG_GET_EVALED_CONTENT) {    PCode p_code =    this_object()->evaled_content = ctx->evaled_p_code = -  PCode (content_type, ctx, subevaler->tag_set, 1); +  PCode (content_type, ctx, subevaler->tag_set, 1, +  evaler->p_code_comp);    if (subevaler->recover_errors)    p_code->recover_errors = 1;    }
4365:    if (id && ctx->misc != ctx->id->misc->defines) \    fatal_error ("ctx->misc != ctx->id->misc->defines\n"); \    ); \ -  if (in_args) args = in_args; \ +  if (in_args) { \ +  args = in_args; \ +  if (stringp (in_args)) \ +  comp->delayed_resolve (this_object(), "args"); \ +  } \    if (in_content) content = in_content; \    ctx->make_p_code = orig_make_p_code; \    if (orig_tag_set) ctx->tag_set = orig_tag_set; \
4930:    context->unwind_state->top = err;    break eval;    } -  if (context->p_code_comp) +  if (p_code && p_code->p_code_comp)    // Fix all delayed resolves in any ongoing p-code compilation. -  context->p_code_comp->compile(); +  p_code->p_code_comp->compile();    LEAVE_CONTEXT();    throw_fatal (err);    }
4971:    context->unwind_state->top = err;    break eval;    } -  if (context->p_code_comp) +  if (p_code && p_code->p_code_comp)    // Fix all delayed resolves in any ongoing p-code compilation. -  context->p_code_comp->compile(); +  p_code->p_code_comp->compile();    LEAVE_CONTEXT();    throw_fatal (err);    }
5198:    return eval();    }    +  constant p_code_comp = 0; +  // To ensure that this identifier is free; other code might do +  // evaler->p_code_comp, where evaler is either a Parser or a PCode. +     Parser _next_free;    // Used to link together unused parser objects for reuse.   
6914:   # define COMP_MSG(X...) do {} while (0)   #endif    + #ifdef DEBUG + static int p_comp_count = 0; + #endif +    static class PikeCompile   //! Helper class to paste together a Pike program from strings. This   //! is thread safe.   { -  + #ifdef DEBUG +  static string pcid = "pc" + ++p_comp_count; + #endif    static int idnr = 0;    static inherit String.Buffer: code;    static inherit Thread.Mutex: mutex;
6927:       string bind (mixed val)    { -  string id = "b" + idnr++; +  string id = + #ifdef DEBUG +  pcid + + #endif +  "b" + idnr++;    COMP_MSG ("%O bind %O to %s\n", this_object(), val, id);    bindings[id] = val;    return id;
6935:       string add_var (string type, void|string init)    { -  string id = "v" + idnr++; +  string id = + #ifdef DEBUG +  pcid + + #endif +  "v" + idnr++;    string txt;       if (init) {
6956:       string add_func (string rettype, string arglist, string def)    { -  string id = "f" + idnr++; +  string id = + #ifdef DEBUG +  pcid + + #endif +  "f" + idnr++;    COMP_MSG ("%O add func: %s %s (%s)\n{%s}\n",    this_object(), rettype, id, arglist, def);    string txt = sprintf ("%s %s (%s)\n{%s}\n", rettype, id, arglist, def);
6971:    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
6985: Inside #if defined(DEBUG)
  #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;    if (zero_type (resolved = bindings[what[index]])) {
7227:    return piece;    }    -  void create (Type _type, Context ctx, void|TagSet _tag_set, void|int collect_results) +  void create (Type _type, Context ctx, void|TagSet _tag_set, void|int collect_results, +  void|PikeCompile _p_code_comp)    // Not static since this is also used to reset p-code objects.    {    if (collect_results) {
7248:    length = 0;    flags |= UPDATED;    protocol_cache_time = -1; -  p_code_comp = ctx->p_code_comp || (ctx->p_code_comp = PikeCompile()); +  p_code_comp = _p_code_comp || PikeCompile();    if (flags & COLLECT_RESULTS) -  PCODE_MSG ("create or reset for result collection\n"); +  PCODE_MSG ("create or reset for result collection (with %s %O)\n", +  _p_code_comp ? "old" : "new", p_code_comp);    else -  PCODE_MSG ("create or reset for content collection\n"); +  PCODE_MSG ("create or reset for content collection (with %s %O)\n", +  _p_code_comp ? "old" : "new", p_code_comp);    }    }   
7282:    // is finished. It's reinstated on entry whenever the p-code is used    // to ensure that the protocol cache doesn't overcache.    -  static PikeCompile p_code_comp; +  PikeCompile p_code_comp; +  // This is inherited by nested PCode instances to make the +  // compilation units larger.       void add (Context ctx, mixed entry, mixed evaled_value)    {
7389:    exec[length + 2] = frame_state;    else {    frame_state = exec[length + 2] = frame->_save(); -  if (stringp (frame->args)) { -  p_code_comp->delayed_resolve (frame, "args"); +  if (stringp (frame->args))    p_code_comp->delayed_resolve (frame_state, 0); -  } +     RESET_FRAME (frame);    }   
7403:    else    exec[length] = frame = exec[length]->_clone_empty();    frame->_restore (exec[length + 2]); -  if (stringp (frame->args)) -  p_code_comp->delayed_resolve (frame, "args"); +     frame->flags = frame_flags;    exec[length + 1] = frame;    }
7888:    if (mixed err = catch {    ctx->make_p_code = 1;    if (!parser) { -  renewed_p_code = PCode (type, ctx, tag_set); +  renewed_p_code = PCode (type, ctx, tag_set, 0, p_code_comp);    renewed_p_code->recover_errors = recover_errors;    renewed_p_code->p_code = new_p_code;    parser = type->get_parser (ctx, tag_set, 0, renewed_p_code);