dougm       00/04/18 15:58:10

  Added:       src/modules/perl modperl_callback.c
  Log:
  implement handler parsing
  get started on basic callbacks
  
  Revision  Changes    Path
  1.1                  modperl-2.0/src/modules/perl/modperl_callback.c
  
  Index: modperl_callback.c
  ===================================================================
  #include "mod_perl.h"
  
  static void require_module(pTHX_ const char *pv)
  {
      SV* sv;
      dSP;
      PUSHSTACKi(PERLSI_REQUIRE);
      PUTBACK;
      sv = sv_newmortal();
      sv_setpv(sv, "require ");
      sv_catpv(sv, pv);
      eval_sv(sv, G_DISCARD);
      SPAGAIN;
      POPSTACK;
  }
  
  modperl_handler_t *modperl_handler_new(ap_pool_t *p, void *h, int type)
  {
      modperl_handler_t *handler = 
          (modperl_handler_t *)ap_pcalloc(p, sizeof(*handler));
  
      switch (type) {
        case MP_HANDLER_TYPE_SV:
          handler->cv = SvREFCNT_inc((SV*)h);
          MpHandlerPARSED_On(handler);
          break;
        case MP_HANDLER_TYPE_CHAR:
          handler->name = (char *)h;
          MP_TRACE_h(MP_FUNC, "new handler %s\n", handler->name);
          break;
      };
  
      ap_register_cleanup(p, (void*)handler,
                          modperl_handler_cleanup, ap_null_cleanup);
  
      return handler;
  }
  
  ap_status_t modperl_handler_cleanup(void *data)
  {
      modperl_handler_t *handler = (modperl_handler_t *)data;
      dTHXa(handler->perl);
      modperl_handler_unparse(aTHX_ handler);
      return APR_SUCCESS;
  }
  
  void modperl_handler_cache_cv(pTHX_ modperl_handler_t *handler, CV *cv)
  {
      if (1) {
          /* XXX: figure out how to invalidate cache
           * e.g. if subroutine is redefined
           */
          handler->cv = SvREFCNT_inc((SV*)cv);
          /* handler->cvgen = MP_sub_generation; */;
      }
      else {
          handler->cv = newSVpvf("%s::%s",
                                 HvNAME(GvSTASH(CvGV(cv))),
                                 GvNAME(CvGV(cv)));
      }
      MP_TRACE_h(MP_FUNC, "caching %s::%s\n",
              HvNAME(GvSTASH(CvGV(cv))),
              GvNAME(CvGV(cv)));
  }
  
  int modperl_handler_lookup(pTHX_ modperl_handler_t *handler,
                             char *class, char *name)
  {
      CV *cv;
      GV *gv;
      HV *stash = gv_stashpv(class, FALSE);
  
      if (!stash) {
          MP_TRACE_h(MP_FUNC, "class %s not defined, attempting to load\n",
                  class);
          require_module(aTHX_ class);
          if (SvTRUE(ERRSV)) {
              MP_TRACE_h(MP_FUNC, "failed to load %s class\n", class);
              return 0;
          }
          else {
              MP_TRACE_h(MP_FUNC, "loaded %s class\n", class);
              if (!(stash = gv_stashpv(class, FALSE))) {
                  MP_TRACE_h(MP_FUNC, "%s package still does not exist\n",
                          class);
                  return 0;
              }
          }
      }
  
      if ((gv = gv_fetchmethod(stash, name)) && (cv = GvCV(gv))) {
          if (CvFLAGS(cv) & CVf_METHOD) { /* sub foo : method {}; */
              MpHandlerMETHOD_On(handler);
              handler->obj = newSVpv(class, 0);
              handler->cv = newSVpv(name, 0);
          }
          else {
              modperl_handler_cache_cv(aTHX_ handler, cv);
          }
  
          MpHandlerPARSED_On(handler);
          MP_TRACE_h(MP_FUNC, "found `%s' in class `%s' as a %s\n",
                  name, HvNAME(stash),
                  MpHandlerMETHOD(handler) ? "method" : "function");
  
          return 1;
      }
      
      MP_TRACE_h(MP_FUNC, "`%s' not found in class `%s'\n",
              name, HvNAME(stash));
  
      return 0;
  }
  
  void modperl_handler_unparse(pTHX_ modperl_handler_t *handler)
  {
      int was_parsed = handler->args || handler->cv || handler->obj;
  
      if (!MpHandlerPARSED(handler)) {
          if (was_parsed) {
              MP_TRACE_h(MP_FUNC, "handler %s was parsed, but not flagged\n",
                      handler->name);
          }
          else {
              MP_TRACE_h(MP_FUNC, "handler %s was never parsed\n", handler->name);
              return;
          }
      }
  
      MpHandlerFLAGS(handler) = 0;
      handler->cvgen = 0;
  
      if (handler->args) {
          av_clear(handler->args);
          SvREFCNT_dec((SV*)handler->args);
          handler->args = Nullav;
      }
      if (handler->cv) {
          SvREFCNT_dec(handler->cv);
          handler->cv = Nullsv;
      }
      if (handler->obj) {
          SvREFCNT_dec(handler->obj);
          handler->obj = Nullsv;
      }
  
      MP_TRACE_h(MP_FUNC, "%s unparsed\n", handler->name);
  }
  
  int modperl_handler_parse(pTHX_ modperl_handler_t *handler)
  {
      char *name = handler->name;
      char *tmp;
      CV *cv;
  
      if (strnEQ(name, "sub ", 4)) {
          handler->cv = eval_pv(name, FALSE);
          MP_TRACE_h(MP_FUNC, "handler is anonymous\n");
          if (!SvTRUE(handler->cv) || SvTRUE(ERRSV)) {
              MP_TRACE_h(MP_FUNC, "eval failed: %s\n", SvPVX(ERRSV));
              handler->cv = Nullsv;
              return 0;
          }
          SvREFCNT_inc(handler->cv);
          MpHandlerANON_On(handler);
          MpHandlerPARSED_On(handler);
          return 1;
      }
      
      if ((tmp = strstr(name, "->"))) {
          char class[256]; /*XXX*/
          int class_len = strlen(name) - strlen(tmp);
          strncpy(class, name, class_len+1);
          class[class_len] = '\0';
  
          MpHandlerMETHOD_On(handler);
          handler->cv = newSVpv(&tmp[2], 0);
  
          if (*class == '$') {
              SV *obj = eval_pv(class, FALSE);
  
              if (SvTRUE(obj)) {
                  handler->obj = SvREFCNT_inc(obj);
                  if (SvROK(obj) && sv_isobject(obj)) {
                      MpHandlerOBJECT_On(handler);
                      MP_TRACE_h(MP_FUNC, "handler object %s isa %s\n",
                              class, HvNAME(SvSTASH((SV*)SvRV(obj))));
                  }
                  else {
                      MP_TRACE_h(MP_FUNC, "%s is not an object, pv=%s\n",
                              class, SvPV_nolen(obj));
                  }
              }
              else {
                  MP_TRACE_h(MP_FUNC, "failed to thaw %s\n", class);
                  return 0;
              }
          }
  
          if (!handler->obj) {
              handler->obj = newSVpv(class, class_len);
              MP_TRACE_h(MP_FUNC, "handler method %s isa %s\n",
                      SvPVX(handler->cv), class);
          }
  
          MpHandlerPARSED_On(handler);
          return 1;
      }
  
      if ((cv = get_cv(name, FALSE))) {
          modperl_handler_cache_cv(aTHX_ handler, cv);
          MpHandlerPARSED_On(handler);
          return 1;
      }
  
      if (modperl_handler_lookup(aTHX_ handler, name, "handler")) {
          return 1;
      }
  
      return 0;
  }
  
  int modperl_callback(pTHX_ modperl_handler_t *handler)
  {
      dSP;
      int count, status;
  
      if (!MpHandlerPARSED(handler)) {
          if (!modperl_handler_parse(aTHX_ handler)) {
              MP_TRACE_h(MP_FUNC, "failed to parse handler `%s'\n",
                      handler->name);
              return HTTP_INTERNAL_SERVER_ERROR;
          }
      }
  
      ENTER;SAVETMPS;
      PUSHMARK(sp);
  
      if (MpHandlerMETHOD(handler)) {
          XPUSHs(handler->obj);
      }
  
      if (handler->args) {
          I32 i, len = AvFILL(handler->args);
          EXTEND(sp, len);
          for (i=0; i<=len; i++) {
              PUSHs(sv_2mortal(*av_fetch(handler->args, i, FALSE)));
          }
      }
  
      PUTBACK;
  
      if (MpHandlerMETHOD(handler)) {
          count = call_method(SvPVX(handler->cv), G_EVAL|G_SCALAR);
      }
      else {
          count = call_sv(handler->cv, G_EVAL|G_SCALAR);
      }
  
      SPAGAIN;
  
      if (count != 1) {
          status = OK;
      }
      else {
          status = POPi;
      }
  
      PUTBACK;
      FREETMPS;LEAVE;
  
      if (SvTRUE(ERRSV)) {
          MP_TRACE_h(MP_FUNC, "$@ = %s\n", SvPVX(ERRSV));
          status = HTTP_INTERNAL_SERVER_ERROR;
      }
  
      return status;
  }
  
  
  

Reply via email to