macros: return or exit

M

Marc Girod

Well, I get errors:

That is... without the debugger, the errors I get are:

$ perl -Mblib blib/script/cleartool.plx des -s foo
Subroutine main::exit redefined at /usr/lib/perl5/site_perl/5.10/
ClearCase/Argv.pm line 21.
Subroutine main::exec redefined at /usr/lib/perl5/site_perl/5.10/
ClearCase/Argv.pm line 22.
Name "DB::single" used only once: possible typo at blib/script/
cleartool.plx line 9, <GEN1> line 16.
foo

Let's say that 'foo' is the expected output.
Only, it is here followed with a standard exit,
so that no following input is read with:

$ cat <<eot | perl -Mblib blib/script/cleartool.plx
des -s foo
des -s bar

Marc
 
M

Marc Girod

That is... without the debugger, the errors I get are:

And under the debugger, in my AutoTrace output,
the first error is:

ClearCase::Wrapper::CODE(0x1ae0ab8)(/cygdrive/o/atcctest/ClearCase-
Wrapper/blib/lib/ClearCase/Wrapper.pm:119):
119: next unless eval "exists \&$tglob";
BEGIN not safe after errors--compilation aborted at /usr/lib/
perl5/5.10/Carp/Heavy.pm line 11.
Compilation failed in require at /usr/lib/perl5/5.10/Carp.pm line 33.
Attempt to reload Carp/Heavy.pm aborted.
Compilation failed in require at /usr/lib/perl5/5.10/Carp.pm line 33.

This is a place in the ClearCase::Wrapper.pm code,
where it examines out of the AutoSplit modules
produced from Wrapper functions, which ones
correspond to 'commands' of the tool being
emulated:

# Now the overlay module is read in. We need to examine its
# newly-created symbol table, determine which functions
# it defined, and import them here. The same basic thing is
# done for the base package later.
my %names = %{"${pkg}::"};
for (keys %names) {
my $tglob = "${pkg}::$_";
# Skip functions that can't be names of valid cleartool ops.
next if m%^_?[A-Z]%;
# Skip typeglobs that don't involve functions. We can only
# do this test under >=5.6.0 since exists() on a coderef
# is a new feature. The eval is needed to avoid a compile-
# time error in <5.6.0.
if ($] >= 5.006) {
next unless eval "exists \&$tglob";
}

The AutoTrace transcript shows earlier 'require Carp;'
but for some reason, doesn't trace its loading,
contrarily to blib, Cwd, strict, Exporter, etc...

This only shows that I don't understand what gets traced.

Marc
 
M

Marc Girod

ClearCase::Wrapper::CODE(0x1ae0ab8)(/cygdrive/o/atcctest/ClearCase-
Wrapper/blib/lib/ClearCase/Wrapper.pm:119):
119:                        next unless eval "exists \&$tglob";

Oh! Now I understood how '$DB::single = 1' works!
So, I could 'put a breakpoint' there.

DB<1> x %names
0 '__ANON__[/usr/lib/perl5/site_perl/5.10/ClearCase/Wrapper/DSB.pm:
13]'
1 *ClearCase::Wrapper::DSB::__ANON__[/usr/lib/perl5/site_perl/5.10/
ClearCase/Wrapper/DSB.pm:13]
2 'lock'
3 *ClearCase::Wrapper::DSB::lock
....
68 'update'
69 *ClearCase::Wrapper::DSB::update
ClearCase::Wrapper::CODE(0x1960c08)(/cygdrive/o/atcctest/ClearCase-
Wrapper/blib/lib/ClearCase/Wrapper.pm:120):
120: next unless eval "exists \&$tglob";
DB<2> p $_
__ANON__[/usr/lib/perl5/site_perl/5.10/ClearCase/Wrapper/DSB.pm:13]
DB<3> s
BEGIN not safe after errors--compilation aborted at /usr/lib/
perl5/5.10/Carp/Heavy.pm line 11.
Compilation failed in require at /usr/lib/perl5/5.10/Carp.pm line 33.
Attempt to reload Carp/Heavy.pm aborted.
Compilation failed in require at /usr/lib/perl5/5.10/Carp.pm line 33.
....

I hope I don't bore you...
Marc
 
M

Marc Girod

It would seem that this problem vanishes
(and leaves place to the original one) if
I change the syntax into either of:

next unless exists &{$tglob};
next unless eval { exists &{$tglob} };

Again I am not quite sure I am not also
changing the semantics... but under the
debugger, it looks like doing what I
understood was intended.

But as I said, I am only back to:

ClearCase-Wrapper> perl -Mblib blib/script/cleartool.plx des -s foo
Subroutine main::exit redefined at /usr/lib/perl5/site_perl/5.10/
ClearCase/Argv.pm line 21.
Subroutine main::exec redefined at /usr/lib/perl5/site_perl/5.10/
ClearCase/Argv.pm line 22.
foo

Marc
 
M

Marc Girod

I don't understand what you are trying to achieve here. What the code
above will actually do is: any code compiled in package 'main' after the
BEGIN block runs, that calls 'exit' or 'exec', will call your custom
subs instead. Is that what you meant?

I have no package explicitely named 'main',
so obviously I have misunderstood something
you wrote earlier.
Now, this makes indeed the 'redefine' errors
'self-explanatory' as you say.
Or... maybe not quite. I understood I was
redefining the meaning of a global 'exec'
in different package contexts.
(Exporting into a specific package
like that is usually a very mad idea.
What if your caller isn't main::
but some other package?)

Sorry, but what is 'main'? Is there a main?
In C, there is a 'main' symbol. It is
garanteed to be unique, in the context of
every program. This is what I understood.

In fact, I would still believe this is OK.
So, it is now that I must be misunderstanding
you...
At least I don't get errors mentioning
'*main::'...

I am writing a wrapper for a command line
tool, so my caller will be 'main'...
The caller is the 'cleartool.plx' script.
When you say 'member' you mean 'method', or perhaps 'function'? Method
calls to ClearCase::Argv->exec won't be affected. Calls to 'exit' or
'exec' from within the ClearCase::Argv package won't be affected, they
will call the builtin as usual.

'Method' is the word Smalltalk used.
'Member' (function) is the word for C++.
'Function' is the C word for something
slightly different (but called 'procedure'
in Fortran). OK, there are functions in Lisp
as well, and there are significant
differences... The terminology is very
confusing. By member, I mean a function
in the restricted namespace of a class
(aka package). In addition, I assume that it
takes a first (syntactically implicit)
argument being a reference to an instance
of the class. This aspect is irrelevant here.
Overrides of builtins must be imported while in another package, in an
attempt to stop you doing it accidentally. That is,

    package Foo;
    *main::exec = sub {...};

will be recognized as an override while in package main, but

    package main;
    *exec = sub {...};

won't. Since I presume your module starts with

    package ClearCase::Argv;

you shouldn't need another package statement inside the BEGIN block.

I have:
- a toplevl script: cleartool.plx (the wrapper)
- a ClearCase::Wrapper module, which does
define some functions, and use exit and exec
in ways I want to override
- a ClearCase::Argv module, which is used to
implement the functions, and offers one 'exec'
among other, which itself uses exit, in a way
which I want to override
- two specialized wrapper modules:
- ClearCase::Wrapper::DSB which defines more
functions (among which the 'des' which I use
in my tests), and does use exit and exec in
ways I want to override.
- ClearCase::Wrapper::MGI of which I am the
author, and thus does *not* use exit nor
exec .
These two modules are 'discovered' dynamically
and loaded by ClearCase::Wrapper.
There could in theory be more of them.

So, my understanding is that I must override
exec and exit in at least 3 packages:
ClearCase::Argv
ClearCase::Wrapper
ClearCase::Wrapper::DSB

(and probably in a 4th one: Argv, which is used
by ClearCase::Argv).

Where do I do this, and how do I prefix the
2 names?
So far, I thought the functions I was overriding
were global main:: functions, and I had to do it
*in* every package.
Obviously, this was wrong...

Thanks
Marc
 
M

Marc Girod

These are not errors, they are warnings.
OK.

They appear to be fairly self-explanatory...

Apart for the fact that in the end, the
behaviour of exit is still the original one...
So that I don't understand what was overridden.
Not what I intended...
That is a separate issue from the warnings, and since I haven't seen
the code I have no idea as to the cause.

*The* code?
If I check it in my Google site, will you read
it (given that you won't be able to run it as
previously explained)?

The previous version is of course already there.
It is only a matter of pushing new increments...

The site is:

http://code.google.com/p/clearcase-cpan/source/browse/#svn/branches

OK... DSB is not there yet, and even if the
changes to the versions there are small,
they are just what I am asking about.

But let's say that within cleartool.plx,
there is a 'one_cmd' function, and the
new code there looks like this:

my $rc = eval { $cmd->(@ARGV) };
if ($@) {
chomp $@;
$rc = $@;
} else {
warn "Normal return: $rc";
}
# ... and exit unless it returned zero.
return $rc;

And there is a loop a bit later such as:

my $rc;
require Text::parseWords;
while (my $line = <>) {
chomp $line;
last if $line eq 'quit';
local @ARGV = Text::parseWords::shellwords($line);
$rc = one_cmd;
}
exit $rc;

The problem so far being that in the case of 'des',
$cmd->(@ARGV) never returns or dies: it just exits.

I am sorry: I kind of believe you won't want to dig
into this in that way either.
This is a top-down way, and I believe it is not
the right way to communicate. It is the way to
avoid communications.
I believe we should be able to drive this bottom-up
which requires communications: that we understand
what prevents each other from understanding.

I don't say it is easy. I just believe it is the
only scalable way, because it depends on 'local'
complexity.

Thanks in any case,
Marc
 
I

Ilya Zakharevich

BEGIN {
if ($ENV{CLEARTOOL_PLX_LOOP}) {
package ClearCase::Argv;
*main::exit = sub { die @_, "\n" };
*main::exec = sub { die system(@_), "\n" };
}
}
Compilation failed in require at /usr/lib/perl5/5.10/Carp.pm line 33.
Attempt to reload Carp/Heavy.pm aborted.

(At least with older versions of Perl) this means that a warning/error
is generated when "sitting deep in some tricky compilataion phase".
(So your module is not loaded in some "normal way".)
Just induce a harmless warning before loading your module, and/or load
your module early.

Hope this helps,
Ilya
 
M

Marc Girod

Perl starts in package 'main', so any code that comes before a 'package'
statement is compiled in package main. I think you perlhaps need to
review perlmod.

Quite possible indeed. I'll do it.
Hmmm. Not quite. A statement like

    exec "one", "two";

is usually compiled as a builtin. However, if there is a sub in the
current package called 'exec', *and* that sub was exported into the
current package by a different package, then it is compiled as a call to
that sub instead.

OK... The clause *and* is still not quite clear to me,
but I'll try to work on it.
No, not at all. 'main' is just the name of the default package. See
above.
OK.

OK, but since you are writing this as a module you must be at least
implicitly assuming it might be used elsewhere. The usual method is to
use 'caller' in your package's 'import' method, which will tell you
which package the 'use' statement happed from:

The module is there... I believe in order to autoload
the split code... i.e. for performance reasons.
I don't see an other reason for now.
I cannot really see that it would be 'used'.
This is why it does exec and exit...
    package ClearCase::Argv;

    sub import {
        my $pkg = caller;
        {
            no strict 'refs';
            *{"$pkg\::exec"} = sub { ... };
        }
        # whatever else you need to do
        # if you want Exporter to work you can
        goto &Exporter::import;
    }

Since it appears you are working 'backwards', and trying to change the
behaviour of modules that aren't loaded yet, you need to explicitly
export into ClearCase::Wrapper &c.
OK. 'Member' is not usually used in that sense in Perl.

OK. I can just see that all my references only
confuse me.
A function call (or sub call) looks like

    foo(1, 2, 3);

or

    Some::package::foo(1, 2, 3);

The first form will look for a builtin with that name, or a 'sub foo' in
the current package. The second form will always look for a 'sub foo' in
Some::package. A method call looks like

    Some::package->foo(1, 2);
    $obj->foo(1, 2);

and will look for a 'sub foo' in Some::package or anything it inherits
from, and call it with an implicit first argument. Method calls never
compile as builtins, so you can have a method called 'exec' with no
problems.

But it is 'defined' in the same way...

sub exec {...}

whether is is intended as a function or a method?
Something I have to grasp there...
Is this 'exec' called as a method or a function? If it's called as a
method, or if it's always called fully-qualified, you can simply ignore
the fact it's called 'exec'.

Well, I now looked that indeed, it is always called
as a method. Which explains, as you said, that there
is no problem.
If you wish to override CC::W's use of 'exec', this must happen *before*
CC::W is loaded. This probably means you need to switch to using a
module MGI::ClearCase, which does the overrides and *then* loads
ClearCase::Wrapper, and avoid loading CC::W in cleartool.plx.
OK...

I would say you want something like this:

    package MGI::ClearCase;

    use warnings;
    use strict;

    my $exec = sub {...};
    my $exit = sub {...};

    *ClearCase::Wrapper::exec       = $exec;
    *ClearCase::Wrapper::exit       = $exit;
    # leave CC::A::exec, since it's already being redefined
    *ClearCase::Argv::exit          = $exit;
    *ClearCase::Wrapper::DSB::exec  = $exec;
    *ClearCase::Wrapper::DSB::exit  = $exit;

    # 'require' not 'use', so it isn't loaded until after the overrides
    require ClearCase::Wrapper;

    # more stuff

    1;

Because of the require, you don't need a BEGIN block?
With a BEGIN block, I can 'use' the module?
You shouldn't get any 'redefined' warnings from this; if you do, it
indicates a problem.
No, they aren't functions (that is, Perl subs) at all, they're builtins.

OK. builtins are not functions!
Terminology again...
They are recognised specially by the compiler, and compiled into special
ops, *unless* they have been overridden at that point.

OK.

Now, I committed (produced) this:

Foo> cat foo
#!/usr/bin/perl -w

use strict;
BEGIN {
package Bar;
*Foo::exit = sub { die 'Died ', @_, "\n" }
}
use Foo;

my $foo = new Foo;
$foo->foo;
print "foo end\n";
exit 0;
Foo> perl -I. foo; echo $?
Foo:foo
Died 85
255

It looks already close...
Thanks a lot for your patience!

Marc
 
M

Marc Girod

That is correct; but reducing the complexity of the problem is *your*
job, not mine.

Well, we managed more or less.
I got something to work now: thanks a lot!
I still have to clean it somewhat.

A few notes:
- @ARGV: I don't think I can change that. It is too
deep in the code I want to reuse, without changing
it; and not without reasons and advantages, in
terms of preserving the context of use of the
tool being emulated. This allows to switch back
and forth, e.g. for debugging purposes.
- How did I know: because I debugged it.
- $cmd: I am again walking from somewhere... I don't
clearly see the gain in suggesting to the original
author a change there...
- main::exit redefinition: I got it now, thanks to you

So, big thanks again.
I have an other question (and others coming for sure)
but it is for an other thread!
Marc
 
M

Marc Girod

I have an other question (and others coming for sure)
but it is for an other thread!

I found the answer to that one, but found another
which is more of a continuation of this thread...

I looked now at the last module I had forgotten
until now: Argv.
This one does define an 'exec' method, in which it
uses the 'exec' builtin.
If I override 'exec' prior to requiring the module,
I do get a warning for the redefinition, i.e. the
definition of the method.
Am I right to believe it is safe for me to ignore
it? The method will get defined, and use my own
overriding instead of the builtin?

It is a bit of a tricky fallback case when this one
would actually be invoked, if ever in the context
of the wrapper (this Argv module is not restricted
to be used for ClearCase...)

Marc
 
M

Marc Girod

Am I right to believe it is safe for me to ignore
it? The method will get defined, and use my own
overriding instead of the builtin?

I believe I answered my own question:

ClearCase-Wrapper> cat ~/tmp/perl/redef
#!/usr/bin/perl -w

# no warnings qw(redefine);

my $exec = sub { print "my exec\n"; };
package Dummy;
*Foo::exec = $exec;
package Foo;
sub exec { print "Foo::exec: "; exec @_; }
Foo::exec '/bin/date';
print "foo\n";
ClearCase-Wrapper> ~/tmp/perl/redef
Subroutine Foo::exec redefined at /home/emagiro/tmp/perl/redef line 7.
my exec
foo

and I believe the asnwer is 'yes'.

Marc
 
M

Marc Girod

No, you haven't, and no, it isn't :(.

Life is hard and then you die...
The test is wrong because the 'sub exec' happens before the '*Foo::exec
= $exec'. Since this is all one file, the sub is compiled at compile
time and the assignment happens at run time, so your override gets
called.
OK.

(Note that the original sub didn't get called, though, so this
still isn't quite what you need.)

You mean that I should have noticed that the output wasn't:

Foo::exec: my exec
foo

Right?
Well yes. This is not what I wanted.

I can see that, as Foo is a package and not a class,
Foo::exec is a function and not a method, so that
there is a recusion in the code before any override.
So, there is no solution to discriminate the two in
this test. But fortunately, there is in the real
case...

In the meanwhile, I got a similar problem with my
real code, as I overrode my own member data, because
I had named it $exec, so that it matched *exec...
However, if you put the assignment in a BEGIN block (as in your real
setup) you will find that the 'sub exec' completely wipes out your
override.

OK: I get the recursion effect 'back'.
The only way around this is to monkey-patch Argv after you
load it, and replace &Argv::exec with a copy of that sub that calls your
wrapper instead of the builtin. That is, you need
    BEGIN {
        require Argv;
        no warnings "redefine";
        *Argv::exec = sub {
            # here copy the body of 'sub exec' from Argv.pm, but replace
            # the two calls to 'exec(@cmd)' with '$exec->(@cmd)'.
        };
    }
before anything else tries to use Argv.

Argh. This is ugly (no offense).
But I can see that it might indeed be the only way.
I.e. I do not override the builtin at all, but
only the method, right?
I just don't care anymore for the builtin...

And this is only for Argv, for the other cases,
the previous scheme should work?
I have to sleep over this.
Thanks again...

Marc
 
M

Marc Girod

Yes, that's right.

Fortunately, in that case, I have an other
solution.
I can redefine:

sub exec {
my $self = shift;
die $self->system(@_);
}

Nobody else uses the naked 'exec' in that scope.

I.e. I am afraid that the current situation:

die system(@_)

even if it does pass the object reference as first
parameter, ends up calling the builtin?

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top