Macro for common OP checks: "is this X or was it before NULLing?"
authorSteffen Mueller <smueller@cpan.org>
Fri, 21 Feb 2014 17:58:04 +0000 (18:58 +0100)
committerSteffen Mueller <smueller@cpan.org>
Wed, 26 Feb 2014 20:05:27 +0000 (21:05 +0100)
For example,

    if (OP_TYPE_IS_OR_WAS(o, OP_LIST))
        ...

is now available instead of either of the following:

    if ( o
         && ( o->op_type == OP_LIST
              || (o->op_type == OP_NULL
                  && o->op_targ == OP_LIST) ) )
        ...

    if ( o &&
         (o->op_type == OP_NULL ? o->op_targ ? o->op_type) == OP_LIST )
        ...

In case the above logic is a bit unclear: It checks whether that OP is
an OP_LIST or used to be one before being NULLed using op_null.
(FTR, the resulting OP_NULLs have their op_targ set to the old OP type).

This sort of check (and it's reverse "isn't and didn't use to be") are a
relatively common pattern in the part of op.c that tries to intuit
structures from optimization-mangled OP trees. Hopefully, using these
macros will make some code a fair amount clearer.

op.c
op.h

diff --git a/op.c b/op.c
index 7bbae54..f17216c 100644 (file)
--- a/op.c
+++ b/op.c
@@ -5777,8 +5777,7 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
        o = newBINOP(OP_AASSIGN, flags, list(force_list(right)), curop);
        o->op_private = (U8)(0 | (flags >> 8));
 
-       if ((left->op_type == OP_LIST
-            || (left->op_type == OP_NULL && left->op_targ == OP_LIST)))
+       if (OP_TYPE_IS_OR_WAS(left, OP_LIST))
        {
            OP* lop = ((LISTOP*)left)->op_first;
            maybe_common_vars = FALSE;
diff --git a/op.h b/op.h
index ed9609d..a1c3c59 100644 (file)
--- a/op.h
+++ b/op.h
@@ -1006,8 +1006,23 @@ to the registree to ensure it is accurate.  The value returned will be
 one of the OA_* constants from op.h.
 
 =for apidoc Am|bool|OP_TYPE_IS|OP *o, Optype type
-Returns true if the given OP is not NULL and if it is of the given
-type.
+Returns true if the given OP is not a NULL pointer
+and if it is of the given type.
+
+The negation of this macro, C<OP_TYPE_ISNT> is also available
+as well as C<OP_TYPE_IS_NN> and C<OP_TYPE_ISNT_NN> which elide
+the NULL pointer check.
+
+=for apidoc Am|bool|OP_TYPE_IS_OR_WAS|OP *o, Optype type
+Returns true if the given OP is not a NULL pointer and
+if it is of the given type or used to be before being
+replaced by an OP of type OP_NULL.
+
+The negation of this macro, C<OP_TYPE_ISNT_AND_WASNT>
+is also available as well as C<OP_TYPE_IS_OR_WAS_NN>
+and C<OP_TYPE_ISNT_AND_WASNT_NN> which elide
+the NULL pointer check.
+
 =cut
 */
 
@@ -1022,7 +1037,27 @@ type.
                     : (PL_opargs[(o)->op_type] & OA_CLASS_MASK))
 
 #define OP_TYPE_IS(o, type) ((o) && (o)->op_type == (type))
-
+#define OP_TYPE_IS_NN(o, type) ((o)->op_type == (type))
+#define OP_TYPE_ISNT(o, type) ((o) && (o)->op_type != (type))
+#define OP_TYPE_ISNT_NN(o, type) ((o)->op_type != (type))
+
+#define OP_TYPE_IS_OR_WAS_NN(o, type) \
+    ( ((o)->op_type == OP_NULL \
+       ? (o)->op_targ \
+       : (o)->op_type) \
+      == (type) )
+
+#define OP_TYPE_IS_OR_WAS(o, type) \
+    ( (o) && OP_TYPE_IS_OR_WAS_NN(o, type) )
+
+#define OP_TYPE_ISNT_AND_WASNT_NN(o, type) \
+    ( ((o)->op_type == OP_NULL \
+       ? (o)->op_targ \
+       : (o)->op_type) \
+      != (type) )
+
+#define OP_TYPE_ISNT_AND_WASNT(o, type) \
+    ( (o) && OP_TYPE_ISNT_AND_WASNT_NN(o, type) )
 
 #define newATTRSUB(f, o, p, a, b) Perl_newATTRSUB_x(aTHX_  f, o, p, a, b, FALSE)
 #define newSUB(f, o, p, b)     newATTRSUB((f), (o), (p), NULL, (b))