This is a japh using duff's device. If you want to understand it, make sure you know what
duff's device is (google it or see http://www.faqs.org/docs/jargon/D/Duff's-device.html).
This may fail if perl is compiled with long doubles or some 64bitness.
I run it successfully with perl5.{00503(if deleted "no warnings"),6.1,8.0,8.1} on i686-linux and perl5.6.1 on sun4-solaris (sparc).
# the usual declarations you use in evry program
no warnings; no strict;
# initialize arrays
@a= (
2.9230032746618539e+48, 3.7537584144024476e+255,
8.7398998774645058e+245, 5.7277807836951398e+250,
1.4916681462400519e-154, 1.7573882009934739e+159,
7.2294757342932134e+221, 4.7379092172264014e+226,
5.7277807836951398e+250, 9.1248812352446012e+192,
3.2418090381883485e+178, 1.3336028865760046e+241,
1.4916681462400519e-154, 2.315841784746365e+77,
3.2418090381883485e+178, 1.3336028865760046e+241,
1.6832434884955199e+212, 1.4916681462400519e-154,
9.1248812352446012e+192, 1.7573882009934739e+159,
7.5479248496432486e+168, 2.5684257331779778e+207,
3.2418090381883485e+178, 1.3336028865760046e+241,
1.6259745436952359e-260
);
tie @b, &^Px$^P;
# with duff's device I can copy array very efficently
goto ("case" . (@a % 8));
while ($a < @a) {
case0: $b[$b++] = $a[$a++];
case7: $b[$b++] = $a[$a++];
case6: $b[$b++] = $a[$a++];
case5: $b[$b++] = $a[$a++];
case4: $b[$b++] = $a[$a++];
case3: $b[$b++] = $a[$a++];
case2: $b[$b++] = $a[$a++];
case1: $b[$b++] = $a[$a++];
};
# oh, I nearly forgot magical subs, so here they are
sub DESTROY { }
sub AUTOLOAD {
($#, %c)= qw(%c $#);
print $_[2];
bless [];
}
__END__
I wonder if you can see how it works,
especially what the two magic subs do.
If you know, please reply me.
(Is this too trivial?)
Re: Fun with duff's device and AUTOLOAD
by Abigail-II (Bishop) on Feb 24, 2004 at 09:59 UTC
|
Copying? ;-) I don't think anything ends up in @b.
Let's analyze the program. First you initialize @a
with some large and small numbers. Then you tie @b -
but to what, one may ask. Well, the result of &^Px$^P. It's not really relevant what &^P returns
(an object - the result of AUTOLOAD), because $^P is essentially 0, leading to &^Px$^P being an empty
string. Hence, it's tied into the class main.
This causes main::TIEARRAY to be called, which is
taken care off by AUTOLOAD - which returns an empty
object blessed into the main class. Now Duff's device copies
the elements of @a into @b one by one. However, since @b is
tied, instead of storing the elements in @b, main::STORE is called. Again, this is taken care
of by AUTOLOAD, but it doesn't store anything, it
just returns another blessed object (which will be discarded
right away).
What remains is the two subs. First the empty DESTROY makes
sure destruction of the many objects created doesn't trigger
AUTOLOAD to be called. Then AUTOLOAD. The first line sets
$# to the value %c. This influences the
way how Perl stringifies numbers (basically by doing
sprintf "%c", $number;). It also sets %c,
but that hash is never accessed. Then it prints $_[2], which, in the case of STORE, contains the
number to be stored. Due to the way stringification has
been altered, it results into garbage on my screen. Perhaps
with another printing device a sensible message appears
(I suspect the numbers in @a not to be random).
And that's it. Nothing is going to be stored in @b.
Abigail | [reply] [d/l] |
|
| [reply] |
Re: Fun with duff's device and AUTOLOAD
by ambrus (Abbot) on Feb 24, 2004 at 21:30 UTC
|
Now I've got 3 different reports that
the script does not work.
Someone said it even did not work on Solaris.
I can not reproduce the bad behaivior so it will
be difficult to track down the bug, especially because
it seems to run on most machines.
(Update @ 2006 may 24: it seems I could finally reproduce the error on a machine. I'll try to investigate it if I have time.)
I'll however explain where I've got
the numbers and why they translate to a sensible message.
I've got the basic idea after the thread $#="%c"; possible bug.
The trick is that
$#="%c"; print 3.14;
is entirely unlike
printf "%c", 3.14;.
Just try
$#="%d"; print 10;!
It does not print 10, rather, it prints 0 (i386) or some stupid
number depending on the endianndess of your cpu.
This is because when formatting numbers a la $#,
perl calls sprintf (or some equivalent) with $# as a format
and the number as a floating point number to the stack.
I don't know where this is in the source, probably hidden somewhere in
sv.c, but I'm quite sure it works like this.
Update: I'd be glad if someone could point me to the guilty code.
The floating-point numbers are given so that when represented as
a double, their upper and lower 4 bytes are both the same small integer,
the ascii code of a character in the message. I had to put the same
integer twice, so that the code would work in both intel-endian and
sparc-endian cpu's. Really, if you consider only one kind of cpus, half
as much numbers would have been enough. Let me show this.
Type perl -we 'print join ", ", unpack "d*", pack "l*",
unpack "c*", "perl"; print $/;'. This prints (on intel)
2.14321574942828e-312, 2.29175545480573e-312.
Now using this numbers you can write perl -we '$#="%c%c";
print 2.14321574942828e-312, 2.29175545480573e-312; print $/;',
which prints perl. I created the obfu in a very similar way.
The problem is that all this will fail if your perl was compiled with
long double support, as the floating point format is quite different. There
may be other problems why the script did not run for some people, which I
currently don't know. Please check that your perl is not compiled with
long doubles. Just run
use Config; print qq[@Config{"nvtype","nvsize"}\n];
if it prints double 8, that is good. If it prints
long double 12, the obfu will not work.
There may also be issues about 64-bitness, I can not check that.
Note that I have not tried the obfu from Windows, but in theory it should
work.
| [reply] [d/l] [select] |
|
I get "double 8" on ActiveState under win2k, yet the obfu doesn't work.
It does seem to have some issue with %c not working the same as printf, though. Printing 65 gives a nul character output, not 'A'. Printing 3.14 gives the bytes FD 91 BA B8 94 9F. Your print 2.14321574942828e-312, 2.29175545480573e-312; example prints "perl" as expected!
| [reply] |
|
Maybe I wasn't clear in the explanation.
$#="%c";print 65; should not print
an A. It pushes 65.0 (a double) and "%x" on the C stack, and
calls sprintf. Then sprintf sees "%c", so tries to pop an int
from the stack. 65.0 is represented as the bytes
(00 00 00 00 00 40 50 40)hex, but an int is just 4 bytes so
it pops (00 00 00 00)hex which is 0 as an int so it prints a nul char.
(On sparc, however, it will get (40 50 40 00)hex, which is 67437568,
so it will either print a null character or a multibyte
character sequence corresponding to unicode chr 67437568,
depending on the version of Perl.)
Dlugosz, I guess your cpu is an x86, correct me if it isn't.
| [reply] [d/l] |
Re: Fun with duff's device and AUTOLOAD
by John M. Dlugosz (Monsignor) on Feb 24, 2004 at 16:06 UTC
|
I tried it under Win2k, and got unreadable output.
I wonder, what is printf("%c", 2.92e48) supposed to do? There is no character with that number, so I would hope for an error.
I'm not sure where it would "naturally" wrap, if that's what it's doing, since the range of legal Unicode ordinals is not a neat power of two. (I'm getting Unicode code points displayed for something like printf '%c',0x434 even without a use utf8 or other preparation.)
I'd be interested to know just what yours is doing. | [reply] [d/l] [select] |
Re: Fun with duff's device and AUTOLOAD
by Skylark (Beadle) on Feb 25, 2004 at 16:59 UTC
|
In case you're curious, as no one has mentioned what the erroneous output is yet, here's the output on my Windows NT4 machine: output : Jwuv borvjgt Pgtn jbdmgt
expected: Just Another Perl Hacker
Though the spaces in the output are not spaces, they are little black arrows pointing up (▲ = HTML entity code #9650)
So I can see you're getting at it, but it doesn't seem to give the right results for each letter... Some seem to work (J in Just, P in Perl) but most do not.
I would like to see it work though... I'll try it on my Linux machine at home.
J-S | [reply] [d/l] |
|
| [reply] |
|
|