&CORE::gmtime() and &CORE::localtime()
authorFather Chrysostomos <sprout@cpan.org>
Fri, 26 Aug 2011 16:49:37 +0000 (09:49 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Fri, 26 Aug 2011 16:49:37 +0000 (09:49 -0700)
This commit allows &CORE::gmtime and &CORE::localtime to be called
through references and via ampersand syntax.  pp_gmtime is modified
to take into account the nulls pushed on to the stack in pp_coreargs,
which happens because pp_coreargs has no other way to tell pp_gmtime
how many arguments it’s actually getting.

I was going to say ‘see commit f6a1686942 for more details’, but found
out, to my horror, that most of the commit message was cut off.  I
don’t know which commit-munging part of git is responsible, but I’ve
had similar problems with git am and git commit --amend.  But, then,
this could have been sloppy copy and paste.  Anyway, here is the
missing part:

Usually, an op that has optional arguments has the number of arguments
indicated with flags on the op itself:

$ ./perl -Ilib -MO=Concise -e 'binmode 1'
6  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
5     <@> binmode vK/1 ->6
-        <0> ex-pushmark s ->3
4        <1> rv2gv sK*/1 ->5
3           <$> gv(*1) s ->4
-e syntax OK
$ ./perl -Ilib -MO=Concise -e 'binmode 1,2'
7  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
6     <@> binmode vK/2 ->7
-        <0> ex-pushmark s ->3
4        <1> rv2gv sK*/1 ->5
3           <$> gv(*1) s ->4
5        <$> const(IV 2) s ->6
-e syntax OK

Notice the /1 vs /2 on the binmode op.

With a CORE sub, we have a single op for both cases.  So, what this
commit does is set the number of arguments to the maximum, push nulls
on to the stack in pp_coreargs (actually, it was already set up to
do it, so there is no change there), and have the pp_ functions for
each op that has optional arguments do a null check after popping
the stack.

pp_binmode already does a null check, but other pp_ functions will
need to be modified.  Since each one is different, those will come in
separate commits.

This is what &CORE::binmode’s op tree looks like:

$ ./perl -Ilib -MO=Concise,CORE::binmode -e 'BEGIN{\&CORE::binmode}'
CORE::binmode:
3  <1> leavesub[1 ref] K/REFC,1 ->(end)
2     <@> binmode sK/2 ->3
-        <0> ex-pushmark s ->1
1        <$> coreargs(IV 212) s ->2
-e syntax OK

gv.c
pp_sys.c
t/op/coresubs.t

diff --git a/gv.c b/gv.c
index e229425..46d5ce0 100644 (file)
--- a/gv.c
+++ b/gv.c
@@ -1354,9 +1354,8 @@ Perl_gv_fetchpvn_flags(pTHX_ const char *nambeg, STRLEN full_len, I32 flags,
            case KEY_chdir:
            case KEY_chomp: case KEY_chop:
            case KEY_each: case KEY_eof: case KEY_exec:
-           case KEY_gmtime:
            case KEY_index: case KEY_keys:
-           case KEY_localtime: case KEY_lock: case KEY_lstat:
+           case KEY_lock: case KEY_lstat:
            case KEY_mkdir: case KEY_open: case KEY_pop:
            case KEY_push: case KEY_rand: case KEY_read:
            case KEY_recv: case KEY_reset:
index 231388f..e6ff355 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -4479,7 +4479,7 @@ PP(pp_gmtime)
        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 
-    if (MAXARG < 1) {
+    if (MAXARG < 1 || (!TOPs && ((void)POPs, 1))) {
        time_t now;
        (void)time(&now);
        when = (Time64_T)now;
index 3339f4f..fcae6b8 100644 (file)
@@ -370,6 +370,10 @@ test_proto "get$_" for qw '
   pwent pwnam pwuid servbyname servbyport servent sockname sockopt
 ';
 
+test_proto 'gmtime';
+&CORE::gmtime;
+pass '&gmtime without args does not crash'; ++$tests;
+
 test_proto 'hex', ff=>255;
 test_proto 'int', 1.5=>1;
 test_proto 'ioctl';
@@ -390,6 +394,11 @@ test_proto 'lcfirst', 'AA', 'aA';
 test_proto 'length', 'aaa', 3;
 test_proto 'link';
 test_proto 'listen';
+
+test_proto 'localtime';
+&CORE::localtime;
+pass '&localtime without args does not crash'; ++$tests;
+
 test_proto 'log';
 test_proto "msg$_" for qw( ctl get rcv snd );