Ap |void |sys_intern_init
#endif
+AopP |const XOP * |custom_op_xop |NN const OP *o
ApR |const char * |custom_op_name |NN const OP *o
ApR |const char * |custom_op_desc |NN const OP *o
+Aop |void |custom_op_register |NN Perl_ppaddr_t ppaddr \
+ |NN const XOP *xop
Adp |void |sv_nosharing |NULLOK SV *sv
Adpbm |void |sv_nolocking |NULLOK SV *sv
#define PL_curstname (vTHX->Icurstname)
#define PL_custom_op_descs (vTHX->Icustom_op_descs)
#define PL_custom_op_names (vTHX->Icustom_op_names)
+#define PL_custom_ops (vTHX->Icustom_ops)
#define PL_cv_has_eval (vTHX->Icv_has_eval)
#define PL_dbargs (vTHX->Idbargs)
#define PL_debstash (vTHX->Idebstash)
#define PL_Icurstname PL_curstname
#define PL_Icustom_op_descs PL_custom_op_descs
#define PL_Icustom_op_names PL_custom_op_names
+#define PL_Icustom_ops PL_custom_ops
#define PL_Icv_has_eval PL_cv_has_eval
#define PL_Idbargs PL_dbargs
#define PL_Idebstash PL_debstash
static opclass
cc_opclass(pTHX_ const OP *o)
{
+ bool custom = 0;
+
if (!o)
return OPc_NULL;
return OPc_PADOP;
#endif
- switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
+ if (o->op_type == OP_CUSTOM)
+ custom = 1;
+
+ switch (OP_CLASS(o)) {
case OA_BASEOP:
return OPc_BASEOP;
* and the SV is a reference to a swash
* (i.e., an RV pointing to an HV).
*/
- return (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
+ return (!custom &&
+ (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
+ )
#if defined(USE_ITHREADS) \
&& (PERL_VERSION > 8 || (PERL_VERSION == 8 && PERL_SUBVERSION >= 9))
? OPc_PADOP : OPc_PVOP;
return OPc_PVOP;
}
warn("can't determine class of operator %s, assuming BASEOP\n",
- PL_op_name[o->op_type]);
+ OP_NAME(o));
return OPc_BASEOP;
}
ALIAS:
desc = 1
CODE:
- RETVAL = (char *)(ix ? PL_op_desc : PL_op_name)[o->op_type];
+ RETVAL = (char *)(ix ? OP_DESC(o) : OP_NAME(o));
OUTPUT:
RETVAL
Perl_croak_xs_usage
Perl_custom_op_desc
Perl_custom_op_name
+Perl_custom_op_register
+Perl_custom_op_xop
Perl_cv_const_sv
Perl_cv_get_call_checker
Perl_cv_set_call_checker
/* Everything that folds to a character, for case insensitivity regex matching */
PERLVARI(Iutf8_foldclosures, HV *, NULL)
+PERLVAR(Icustom_ops, HV *) /* custom op registrations */
+
/* If you are adding a U8 or U16, check to see if there are 'Space' comments
* above on where there are gaps which currently will be structure padding. */
return sv_2bool_flags(sv, SV_GMAGIC);
}
+
+const char*
+Perl_custom_op_name(pTHX_ const OP* o)
+{
+ PERL_ARGS_ASSERT_CUSTOM_OP_NAME;
+ return XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_name);
+}
+
+const char*
+Perl_custom_op_desc(pTHX_ const OP* o)
+{
+ PERL_ARGS_ASSERT_CUSTOM_OP_DESC;
+ return XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_desc);
+}
#endif /* NO_MATHOMS */
/*
assert (!cPMOP->op_pmstashstartu.op_pmreplstart);
}
break;
+
+ case OP_CUSTOM: {
+ Perl_cpeep_t cpeep =
+ XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_peep);
+ if (cpeep)
+ cpeep(aTHX_ o, oldop);
+ break;
+ }
+
}
oldop = o;
}
CALL_RPEEP(o);
}
-const char*
-Perl_custom_op_name(pTHX_ const OP* o)
+const XOP *
+Perl_custom_op_xop(pTHX_ const OP *o)
{
- dVAR;
- const IV index = PTR2IV(o->op_ppaddr);
- SV* keysv;
- HE* he;
+ SV *keysv;
+ HE *he = NULL;
+ XOP *xop;
+
+ static const XOP xop_null = { 0, 0, 0, 0, 0 };
- PERL_ARGS_ASSERT_CUSTOM_OP_NAME;
+ PERL_ARGS_ASSERT_CUSTOM_OP_XOP;
+ assert(o->op_type == OP_CUSTOM);
- if (!PL_custom_op_names) /* This probably shouldn't happen */
- return (char *)PL_op_name[OP_CUSTOM];
+ /* This is wrong. It assumes a function pointer can be cast to IV,
+ * which isn't guaranteed, but this is what the old custom OP code
+ * did. In principle it should be safer to Copy the bytes of the
+ * pointer into a PV: since the new interface is hidden behind
+ * functions, this can be changed later if necessary. */
+ /* Change custom_op_xop if this ever happens */
+ keysv = sv_2mortal(newSViv(PTR2IV(o->op_ppaddr)));
- keysv = sv_2mortal(newSViv(index));
+ if (PL_custom_ops)
+ he = hv_fetch_ent(PL_custom_ops, keysv, 0, 0);
+
+ /* assume noone will have just registered a desc */
+ if (!he && PL_custom_op_names &&
+ (he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0))
+ ) {
+ const char *pv;
+ STRLEN l;
+
+ /* XXX does all this need to be shared mem? */
+ Newx(xop, 1, XOP);
+ pv = SvPV(HeVAL(he), l);
+ XopENTRY_set(xop, xop_name, savepvn(pv, l));
+ if (PL_custom_op_descs &&
+ (he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0))
+ ) {
+ pv = SvPV(HeVAL(he), l);
+ XopENTRY_set(xop, xop_desc, savepvn(pv, l));
+ }
+ Perl_custom_op_register(aTHX_ o->op_ppaddr, xop);
+ return xop;
+ }
- he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0);
- if (!he)
- return (char *)PL_op_name[OP_CUSTOM]; /* Don't know who you are */
+ if (!he) return &xop_null;
- return SvPV_nolen(HeVAL(he));
+ xop = INT2PTR(XOP *, SvIV(HeVAL(he)));
+ return xop;
}
-const char*
-Perl_custom_op_desc(pTHX_ const OP* o)
-{
- dVAR;
- const IV index = PTR2IV(o->op_ppaddr);
- SV* keysv;
- HE* he;
- PERL_ARGS_ASSERT_CUSTOM_OP_DESC;
- if (!PL_custom_op_descs)
- return (char *)PL_op_desc[OP_CUSTOM];
+void
+Perl_custom_op_register(pTHX_ Perl_ppaddr_t ppaddr, const XOP *xop)
+{
+ SV *keysv;
+
+ PERL_ARGS_ASSERT_CUSTOM_OP_REGISTER;
- keysv = sv_2mortal(newSViv(index));
+ /* see the comment in custom_op_xop */
+ keysv = sv_2mortal(newSViv(PTR2IV(ppaddr)));
- he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0);
- if (!he)
- return (char *)PL_op_desc[OP_CUSTOM];
+ if (!PL_custom_ops)
+ PL_custom_ops = newHV();
- return SvPV_nolen(HeVAL(he));
+ if (!hv_store_ent(PL_custom_ops, keysv, newSViv(PTR2IV(xop)), 0))
+ Perl_croak(aTHX_ "panic: can't register custom OP %s", xop->xop_name);
}
#include "XSUB.h"
#define RV2CVOPCV_MARK_EARLY 0x00000001
#define RV2CVOPCV_RETURN_NAME_GV 0x00000002
+struct custom_op {
+ U32 xop_flags;
+ const char *xop_name;
+ const char *xop_desc;
+ U32 xop_class;
+ void (*xop_peep)(pTHX_ OP *o, OP *oldop);
+};
+
+#define XopFLAGS(xop) ((xop)->xop_flags)
+
+#define XOPf_xop_name 0x01
+#define XOPf_xop_desc 0x02
+#define XOPf_xop_class 0x04
+#define XOPf_xop_peep 0x08
+
+#define XOPd_xop_name PL_op_name[OP_CUSTOM]
+#define XOPd_xop_desc PL_op_desc[OP_CUSTOM]
+#define XOPd_xop_class OA_BASEOP
+#define XOPd_xop_peep ((Perl_cpeep_t)0)
+
+#define XopENTRY_set(xop, which, to) \
+ STMT_START { \
+ (xop)->which = (to); \
+ (xop)->xop_flags |= XOPf_ ## which; \
+ } STMT_END
+
+#define XopENTRY(xop, which) \
+ ((XopFLAGS(xop) & XOPf_ ## which) ? (xop)->which : XOPd_ ## which)
+
+#define XopDISABLE(xop, which) ((xop)->xop_flags &= ~XOPf_ ## which)
+#define XopENABLE(xop, which) \
+ STMT_START { \
+ (xop)->xop_flags |= XOPf_ ## which; \
+ assert(XopENTRY(xop, which)); \
+ } STMT_END
+
+#define OP_NAME(o) ((o)->op_type == OP_CUSTOM \
+ ? XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_name) \
+ : PL_op_name[(o)->op_type])
+#define OP_DESC(o) ((o)->op_type == OP_CUSTOM \
+ ? XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_desc) \
+ : PL_op_desc[(o)->op_type])
+#define OP_CLASS(o) ((o)->op_type == OP_CUSTOM \
+ ? XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_class) \
+ : (PL_opargs[(o)->op_type] & OA_CLASS_MASK))
+
#ifdef PERL_MAD
# define MAD_NULL 1
# define MAD_PV 2
START_EXTERN_C
-#define OP_NAME(o) ((o)->op_type == OP_CUSTOM ? custom_op_name(o) : \
- PL_op_name[(o)->op_type])
-#define OP_DESC(o) ((o)->op_type == OP_CUSTOM ? custom_op_desc(o) : \
- PL_op_desc[(o)->op_type])
-
#ifndef DOINIT
EXTCONST char* const PL_op_name[];
#else
typedef struct loop LOOP;
typedef struct block_hooks BHK;
+typedef struct custom_op XOP;
typedef struct interpreter PerlInterpreter;
typedef OP* (*Perl_check_t) (pTHX_ OP*);
typedef void(*Perl_ophook_t)(pTHX_ OP*);
typedef int (*Perl_keyword_plugin_t)(pTHX_ char*, STRLEN, OP**);
+typedef void(*Perl_cpeep_t)(pTHX_ OP *, OP *);
#define KEYWORD_PLUGIN_DECLINE 0
#define KEYWORD_PLUGIN_STMT 1
#define PERL_ARGS_ASSERT_CUSTOM_OP_NAME \
assert(o)
+PERL_CALLCONV void Perl_custom_op_register(pTHX_ Perl_ppaddr_t ppaddr, const XOP *xop)
+ __attribute__nonnull__(pTHX_1)
+ __attribute__nonnull__(pTHX_2);
+#define PERL_ARGS_ASSERT_CUSTOM_OP_REGISTER \
+ assert(ppaddr); assert(xop)
+
+PERL_CALLCONV const XOP * Perl_custom_op_xop(pTHX_ const OP *o)
+ __attribute__pure__
+ __attribute__nonnull__(pTHX_1);
+#define PERL_ARGS_ASSERT_CUSTOM_OP_XOP \
+ assert(o)
+
PERL_CALLCONV void Perl_cv_ckproto_len(pTHX_ const CV* cv, const GV* gv, const char* p, const STRLEN len)
__attribute__nonnull__(pTHX_1);
#define PERL_ARGS_ASSERT_CV_CKPROTO_LEN \
print <<END;
START_EXTERN_C
-#define OP_NAME(o) ((o)->op_type == OP_CUSTOM ? custom_op_name(o) : \\
- PL_op_name[(o)->op_type])
-#define OP_DESC(o) ((o)->op_type == OP_CUSTOM ? custom_op_desc(o) : \\
- PL_op_desc[(o)->op_type])
-
#ifndef DOINIT
EXTCONST char* const PL_op_name[];
#else
PL_modglobal = hv_dup_inc(proto_perl->Imodglobal, param);
PL_custom_op_names = hv_dup_inc(proto_perl->Icustom_op_names,param);
PL_custom_op_descs = hv_dup_inc(proto_perl->Icustom_op_descs,param);
+ PL_custom_ops = hv_dup_inc(proto_perl->Icustom_ops, param);
PL_profiledata = NULL;