Commit | Line | Data |
---|---|---|
9bfdb36e | 1 | #!./perl -w |
92d69e20 IZ |
2 | |
3 | # | |
4 | # test method calls and autoloading. | |
5 | # | |
6 | ||
4755096e GS |
7 | BEGIN { |
8 | chdir 't' if -d 't'; | |
6dc4e5ce JH |
9 | @INC = qw(. ../lib); |
10 | require "test.pl"; | |
4755096e GS |
11 | } |
12 | ||
9bfdb36e NC |
13 | use strict; |
14 | no warnings 'once'; | |
15 | ||
f226e9be | 16 | plan(tests => 98); |
92d69e20 IZ |
17 | |
18 | @A::ISA = 'B'; | |
19 | @B::ISA = 'C'; | |
20 | ||
21 | sub C::d {"C::d"} | |
22 | sub D::d {"D::d"} | |
23 | ||
567ce7b1 | 24 | # First, some basic checks of method-calling syntax: |
9bfdb36e | 25 | my $obj = bless [], "Pack"; |
567ce7b1 | 26 | sub Pack::method { shift; join(",", "method", @_) } |
9bfdb36e | 27 | my $mname = "method"; |
567ce7b1 | 28 | |
6dc4e5ce JH |
29 | is(Pack->method("a","b","c"), "method,a,b,c"); |
30 | is(Pack->$mname("a","b","c"), "method,a,b,c"); | |
31 | is(method Pack ("a","b","c"), "method,a,b,c"); | |
32 | is((method Pack "a","b","c"), "method,a,b,c"); | |
567ce7b1 | 33 | |
6dc4e5ce JH |
34 | is(Pack->method(), "method"); |
35 | is(Pack->$mname(), "method"); | |
36 | is(method Pack (), "method"); | |
37 | is(Pack->method, "method"); | |
38 | is(Pack->$mname, "method"); | |
39 | is(method Pack, "method"); | |
567ce7b1 | 40 | |
6dc4e5ce JH |
41 | is($obj->method("a","b","c"), "method,a,b,c"); |
42 | is($obj->$mname("a","b","c"), "method,a,b,c"); | |
43 | is((method $obj ("a","b","c")), "method,a,b,c"); | |
44 | is((method $obj "a","b","c"), "method,a,b,c"); | |
145eb477 | 45 | |
6dc4e5ce JH |
46 | is($obj->method(0), "method,0"); |
47 | is($obj->method(1), "method,1"); | |
567ce7b1 | 48 | |
6dc4e5ce JH |
49 | is($obj->method(), "method"); |
50 | is($obj->$mname(), "method"); | |
51 | is((method $obj ()), "method"); | |
52 | is($obj->method, "method"); | |
53 | is($obj->$mname, "method"); | |
54 | is(method $obj, "method"); | |
567ce7b1 | 55 | |
6dc4e5ce | 56 | is( A->d, "C::d"); # Update hash table; |
92d69e20 IZ |
57 | |
58 | *B::d = \&D::d; # Import now. | |
6dc4e5ce | 59 | is(A->d, "D::d"); # Update hash table; |
92d69e20 | 60 | |
44a8e56a | 61 | { |
fb73857a | 62 | local @A::ISA = qw(C); # Update hash table with split() assignment |
6dc4e5ce | 63 | is(A->d, "C::d"); |
fb73857a | 64 | $#A::ISA = -1; |
6dc4e5ce | 65 | is(eval { A->d } || "fail", "fail"); |
fb73857a | 66 | } |
6dc4e5ce | 67 | is(A->d, "D::d"); |
fb73857a | 68 | |
69 | { | |
44a8e56a | 70 | local *B::d; |
71 | eval 'sub B::d {"B::d1"}'; # Import now. | |
6dc4e5ce | 72 | is(A->d, "B::d1"); # Update hash table; |
44a8e56a | 73 | undef &B::d; |
6dc4e5ce | 74 | is((eval { A->d }, ($@ =~ /Undefined subroutine/)), 1); |
44a8e56a | 75 | } |
92d69e20 | 76 | |
6dc4e5ce | 77 | is(A->d, "D::d"); # Back to previous state |
92d69e20 | 78 | |
9bfdb36e | 79 | eval 'no warnings "redefine"; sub B::d {"B::d2"}'; # Import now. |
6dc4e5ce | 80 | is(A->d, "B::d2"); # Update hash table; |
92d69e20 IZ |
81 | |
82 | # What follows is hardly guarantied to work, since the names in scripts | |
94e1fbae JK |
83 | # are already linked to "pruned" globs. Say, 'undef &B::d' if it were |
84 | # after 'delete $B::{d}; sub B::d {}' would reach an old subroutine. | |
92d69e20 IZ |
85 | |
86 | undef &B::d; | |
87 | delete $B::{d}; | |
5743f2a3 FC |
88 | is(A->d, "C::d"); |
89 | ||
90 | eval 'sub B::d {"B::d2.5"}'; | |
91 | A->d; # Update hash table; | |
92 | my $glob = \delete $B::{d}; # non-void context; hang on to the glob | |
6dc4e5ce | 93 | is(A->d, "C::d"); # Update hash table; |
92d69e20 IZ |
94 | |
95 | eval 'sub B::d {"B::d3"}'; # Import now. | |
6dc4e5ce | 96 | is(A->d, "B::d3"); # Update hash table; |
92d69e20 IZ |
97 | |
98 | delete $B::{d}; | |
99 | *dummy::dummy = sub {}; # Mark as updated | |
6dc4e5ce | 100 | is(A->d, "C::d"); |
92d69e20 IZ |
101 | |
102 | eval 'sub B::d {"B::d4"}'; # Import now. | |
6dc4e5ce | 103 | is(A->d, "B::d4"); # Update hash table; |
92d69e20 IZ |
104 | |
105 | delete $B::{d}; # Should work without any help too | |
6dc4e5ce | 106 | is(A->d, "C::d"); |
92d69e20 | 107 | |
fae75791 CS |
108 | { |
109 | local *C::d; | |
6dc4e5ce | 110 | is(eval { A->d } || "nope", "nope"); |
fae75791 | 111 | } |
6dc4e5ce | 112 | is(A->d, "C::d"); |
fae75791 | 113 | |
9bfdb36e | 114 | *A::x = *A::d; |
44a8e56a | 115 | A->d; |
9bfdb36e NC |
116 | is(eval { A->x } || "nope", "nope", 'cache should not follow synonyms'); |
117 | ||
118 | my $counter; | |
44a8e56a | 119 | |
92d69e20 IZ |
120 | eval <<'EOF'; |
121 | sub C::e; | |
09280a33 | 122 | BEGIN { *B::e = \&C::e } # Shouldn't prevent AUTOLOAD in original pkg |
92d69e20 IZ |
123 | sub Y::f; |
124 | $counter = 0; | |
125 | ||
54310121 | 126 | @X::ISA = 'Y'; |
dc848c6f | 127 | @Y::ISA = 'B'; |
92d69e20 IZ |
128 | |
129 | sub B::AUTOLOAD { | |
130 | my $c = ++$counter; | |
131 | my $method = $B::AUTOLOAD; | |
09280a33 CS |
132 | my $msg = "B: In $method, $c"; |
133 | eval "sub $method { \$msg }"; | |
134 | goto &$method; | |
92d69e20 IZ |
135 | } |
136 | sub C::AUTOLOAD { | |
137 | my $c = ++$counter; | |
138 | my $method = $C::AUTOLOAD; | |
09280a33 CS |
139 | my $msg = "C: In $method, $c"; |
140 | eval "sub $method { \$msg }"; | |
141 | goto &$method; | |
92d69e20 IZ |
142 | } |
143 | EOF | |
144 | ||
6dc4e5ce JH |
145 | is(A->e(), "C: In C::e, 1"); # We get a correct autoload |
146 | is(A->e(), "C: In C::e, 1"); # Which sticks | |
92d69e20 | 147 | |
6dc4e5ce JH |
148 | is(A->ee(), "B: In A::ee, 2"); # We get a generic autoload, method in top |
149 | is(A->ee(), "B: In A::ee, 2"); # Which sticks | |
92d69e20 | 150 | |
6dc4e5ce JH |
151 | is(Y->f(), "B: In Y::f, 3"); # We vivify a correct method |
152 | is(Y->f(), "B: In Y::f, 3"); # Which sticks | |
92d69e20 IZ |
153 | |
154 | # This test is not intended to be reasonable. It is here just to let you | |
155 | # know that you broke some old construction. Feel free to rewrite the test | |
156 | # if your patch breaks it. | |
157 | ||
9bfdb36e NC |
158 | { |
159 | no warnings 'redefine'; | |
92d69e20 | 160 | *B::AUTOLOAD = sub { |
9bfdb36e | 161 | use warnings; |
92d69e20 | 162 | my $c = ++$counter; |
9bfdb36e NC |
163 | my $method = $::AUTOLOAD; |
164 | no strict 'refs'; | |
165 | *$::AUTOLOAD = sub { "new B: In $method, $c" }; | |
166 | goto &$::AUTOLOAD; | |
92d69e20 | 167 | }; |
9bfdb36e | 168 | } |
92d69e20 | 169 | |
6dc4e5ce JH |
170 | is(A->eee(), "new B: In A::eee, 4"); # We get a correct $autoload |
171 | is(A->eee(), "new B: In A::eee, 4"); # Which sticks | |
fb73857a | 172 | |
9bfdb36e NC |
173 | { |
174 | no strict 'refs'; | |
33f89799 | 175 | no warnings 'deprecated'; |
9bfdb36e | 176 | # this test added due to bug discovery (in 5.004_04, fb73857aa0bfa8ed) |
33f89799 NC |
177 | # Possibly kill this test now that defined @::array is finally properly |
178 | # deprecated? | |
9bfdb36e NC |
179 | is(defined(@{"unknown_package::ISA"}) ? "defined" : "undefined", "undefined"); |
180 | } | |
f6ec51f7 GS |
181 | |
182 | # test that failed subroutine calls don't affect method calls | |
183 | { | |
184 | package A1; | |
185 | sub foo { "foo" } | |
186 | package A2; | |
9bfdb36e | 187 | @A2::ISA = 'A1'; |
f6ec51f7 | 188 | package main; |
6dc4e5ce JH |
189 | is(A2->foo(), "foo"); |
190 | is(do { eval 'A2::foo()'; $@ ? 1 : 0}, 1); | |
191 | is(A2->foo(), "foo"); | |
f6ec51f7 | 192 | } |
c1899e02 | 193 | |
af09ea45 IK |
194 | ## This test was totally misguided. It passed before only because the |
195 | ## code to determine if a package was loaded used to look for the hash | |
196 | ## %Foo::Bar instead of the package Foo::Bar:: -- and Config.pm just | |
197 | ## happens to export %Config. | |
198 | # { | |
6dc4e5ce | 199 | # is(do { use Config; eval 'Config->foo()'; |
af09ea45 | 200 | # $@ =~ /^\QCan't locate object method "foo" via package "Config" at/ ? 1 : $@}, 1); |
6dc4e5ce | 201 | # is(do { use Config; eval '$d = bless {}, "Config"; $d->foo()'; |
af09ea45 IK |
202 | # $@ =~ /^\QCan't locate object method "foo" via package "Config" at/ ? 1 : $@}, 1); |
203 | # } | |
204 | ||
af09ea45 | 205 | # test error messages if method loading fails |
9bfdb36e NC |
206 | my $e; |
207 | ||
2f907243 NC |
208 | eval '$e = bless {}, "E::A"; E::A->foo()'; |
209 | like ($@, qr/^\QCan't locate object method "foo" via package "E::A" at/); | |
210 | eval '$e = bless {}, "E::B"; $e->foo()'; | |
211 | like ($@, qr/^\QCan't locate object method "foo" via package "E::B" at/); | |
212 | eval 'E::C->foo()'; | |
213 | like ($@, qr/^\QCan't locate object method "foo" via package "E::C" (perhaps /); | |
214 | ||
215 | eval 'UNIVERSAL->E::D::foo()'; | |
216 | like ($@, qr/^\QCan't locate object method "foo" via package "E::D" (perhaps /); | |
9bfdb36e | 217 | eval 'my $e = bless {}, "UNIVERSAL"; $e->E::E::foo()'; |
2f907243 | 218 | like ($@, qr/^\QCan't locate object method "foo" via package "E::E" (perhaps /); |
af09ea45 IK |
219 | |
220 | $e = bless {}, "E::F"; # force package to exist | |
2f907243 NC |
221 | eval 'UNIVERSAL->E::F::foo()'; |
222 | like ($@, qr/^\QCan't locate object method "foo" via package "E::F" at/); | |
223 | eval '$e = bless {}, "UNIVERSAL"; $e->E::F::foo()'; | |
224 | like ($@, qr/^\QCan't locate object method "foo" via package "E::F" at/); | |
af09ea45 IK |
225 | |
226 | # TODO: we need some tests for the SUPER:: pseudoclass | |
227 | ||
228 | # failed method call or UNIVERSAL::can() should not autovivify packages | |
6dc4e5ce JH |
229 | is( $::{"Foo::"} || "none", "none"); # sanity check 1 |
230 | is( $::{"Foo::"} || "none", "none"); # sanity check 2 | |
c1899e02 | 231 | |
6dc4e5ce JH |
232 | is( UNIVERSAL::can("Foo", "boogie") ? "yes":"no", "no" ); |
233 | is( $::{"Foo::"} || "none", "none"); # still missing? | |
af09ea45 | 234 | |
6dc4e5ce JH |
235 | is( Foo->UNIVERSAL::can("boogie") ? "yes":"no", "no" ); |
236 | is( $::{"Foo::"} || "none", "none"); # still missing? | |
af09ea45 | 237 | |
6dc4e5ce JH |
238 | is( Foo->can("boogie") ? "yes":"no", "no" ); |
239 | is( $::{"Foo::"} || "none", "none"); # still missing? | |
af09ea45 | 240 | |
6dc4e5ce JH |
241 | is( eval 'Foo->boogie(); 1' ? "yes":"no", "no" ); |
242 | is( $::{"Foo::"} || "none", "none"); # still missing? | |
af09ea45 | 243 | |
6dc4e5ce | 244 | is(do { eval 'Foo->boogie()'; |
af09ea45 IK |
245 | $@ =~ /^\QCan't locate object method "boogie" via package "Foo" (perhaps / ? 1 : $@}, 1); |
246 | ||
247 | eval 'sub Foo::boogie { "yes, sir!" }'; | |
6dc4e5ce JH |
248 | is( $::{"Foo::"} ? "ok" : "none", "ok"); # should exist now |
249 | is( Foo->boogie(), "yes, sir!"); | |
af09ea45 IK |
250 | |
251 | # TODO: universal.t should test NoSuchPackage->isa()/can() | |
c1899e02 | 252 | |
f0670693 SC |
253 | # This is actually testing parsing of indirect objects and undefined subs |
254 | # print foo("bar") where foo does not exist is not an indirect object. | |
255 | # print foo "bar" where foo does not exist is an indirect object. | |
84251760 | 256 | eval 'sub AUTOLOAD { "ok ", shift, "\n"; }'; |
6dc4e5ce | 257 | ok(1); |
af09ea45 | 258 | |
a397c3d9 | 259 | # Bug ID 20010902.002 |
6dc4e5ce | 260 | is( |
a397c3d9 | 261 | eval q[ |
9bfdb36e | 262 | my $x = 'x'; # Lexical or package variable, 5.6.1 panics. |
a397c3d9 RGS |
263 | sub Foo::x : lvalue { $x } |
264 | Foo->$x = 'ok'; | |
265 | ] || $@, 'ok' | |
266 | ); | |
267 | ||
3ad83ce7 AMS |
268 | # An autoloaded, inherited DESTROY may be invoked differently than normal |
269 | # methods, and has been known to give rise to spurious warnings | |
270 | # eg <200203121600.QAA11064@gizmo.fdgroup.co.uk> | |
271 | ||
272 | { | |
273 | use warnings; | |
274 | my $w = ''; | |
275 | local $SIG{__WARN__} = sub { $w = $_[0] }; | |
276 | ||
277 | sub AutoDest::Base::AUTOLOAD {} | |
278 | @AutoDest::ISA = qw(AutoDest::Base); | |
279 | { my $x = bless {}, 'AutoDest'; } | |
280 | $w =~ s/\n//g; | |
6dc4e5ce | 281 | is($w, ''); |
3ad83ce7 AMS |
282 | } |
283 | ||
e189a56d IK |
284 | # [ID 20020305.025] PACKAGE::SUPER doesn't work anymore |
285 | ||
286 | package main; | |
287 | our @X; | |
288 | package Amajor; | |
289 | sub test { | |
290 | push @main::X, 'Amajor', @_; | |
291 | } | |
292 | package Bminor; | |
293 | use base qw(Amajor); | |
294 | package main; | |
295 | sub Bminor::test { | |
296 | $_[0]->Bminor::SUPER::test('x', 'y'); | |
297 | push @main::X, 'Bminor', @_; | |
298 | } | |
299 | Bminor->test('y', 'z'); | |
300 | is("@X", "Amajor Bminor x y Bminor Bminor y z"); | |
301 | ||
0dae17bd GS |
302 | package main; |
303 | for my $meth (['Bar', 'Foo::Bar'], | |
304 | ['SUPER::Bar', 'main::SUPER::Bar'], | |
305 | ['Xyz::SUPER::Bar', 'Xyz::SUPER::Bar']) | |
306 | { | |
307 | fresh_perl_is(<<EOT, | |
308 | package UNIVERSAL; sub AUTOLOAD { my \$c = shift; print "\$c \$AUTOLOAD\\n" } | |
309 | package Xyz; | |
310 | package main; Foo->$meth->[0](); | |
311 | EOT | |
312 | "Foo $meth->[1]", | |
313 | { switches => [ '-w' ] }, | |
314 | "check if UNIVERSAL::AUTOLOAD works", | |
315 | ); | |
316 | } | |
1f15e670 NT |
317 | |
318 | # Test for #71952: crash when looking for a nonexistent destructor | |
319 | # Regression introduced by fbb3ee5af3d4 | |
320 | { | |
321 | fresh_perl_is(<<'EOT', | |
322 | sub M::DESTROY; bless {}, "M" ; print "survived\n"; | |
323 | EOT | |
324 | "survived", | |
325 | {}, | |
326 | "no crash with a declared but missing DESTROY method" | |
327 | ); | |
328 | } | |
329 | ||
da6b625f FC |
330 | # Test for calling a method on a packag name return by a magic variable |
331 | sub TIESCALAR{bless[]} | |
332 | sub FETCH{"main"} | |
333 | my $kalled; | |
334 | sub bolgy { ++$kalled; } | |
335 | tie my $a, ""; | |
336 | $a->bolgy; | |
337 | is $kalled, 1, 'calling a class method via a magic variable'; | |
f937af42 BF |
338 | |
339 | { | |
340 | package NulTest; | |
341 | sub method { 1 } | |
342 | ||
343 | package main; | |
344 | eval { | |
345 | NulTest->${ \"method\0Whoops" }; | |
346 | }; | |
347 | like $@, qr/Can't locate object method "method\0Whoops" via package "NulTest" at/, | |
348 | "method lookup is nul-clean"; | |
349 | ||
350 | *NulTest::AUTOLOAD = sub { our $AUTOLOAD; return $AUTOLOAD }; | |
351 | ||
352 | like(NulTest->${ \"nul\0test" }, "nul\0test", "AUTOLOAD is nul-clean"); | |
353 | } | |
2a0f5ef0 BF |
354 | |
355 | ||
356 | { | |
357 | fresh_perl_is( | |
358 | q! sub T::DESTROY { $x = $_[0]; } bless [], "T";!, | |
359 | "DESTROY created new reference to dead object 'T' during global destruction.", | |
360 | {}, | |
361 | "DESTROY creating a new reference to the object generates a warning." | |
362 | ); | |
363 | } | |
71481574 FC |
364 | |
365 | # [perl #43663] | |
366 | { | |
367 | $::{"Just"} = \1; | |
368 | sub Just::a_japh { return "$_[0] another Perl hacker," } | |
369 | is eval { "Just"->a_japh }, "Just another Perl hacker,", | |
370 | 'constants do not interfere with class methods'; | |
371 | } | |
0865059d FC |
372 | |
373 | # [perl #109264] | |
374 | { | |
375 | no strict 'vars'; | |
376 | sub bliggles { 1 } | |
377 | sub lbiggles :lvalue { index "foo", "f" } | |
378 | ok eval { main->bliggles(my($foo,$bar)) }, | |
379 | 'foo->bar(my($foo,$bar)) is not called in lvalue context'; | |
380 | ok eval { main->bliggles(our($foo,$bar)) }, | |
381 | 'foo->bar(our($foo,$bar)) is not called in lvalue context'; | |
382 | ok eval { main->bliggles(local($foo,$bar)) }, | |
383 | 'foo->bar(local($foo,$bar)) is not called in lvalue context'; | |
384 | ok eval { () = main->lbiggles(my($foo,$bar)); 1 }, | |
385 | 'foo->lv(my($foo,$bar)) is not called in lvalue context'; | |
386 | ok eval { () = main->lbiggles(our($foo,$bar)); 1 }, | |
387 | 'foo->lv(our($foo,$bar)) is not called in lvalue context'; | |
388 | ok eval { () = main->lbiggles(local($foo,$bar)); 1 }, | |
389 | 'foo->lv(local($foo,$bar)) is not called in lvalue context'; | |
390 | } | |
544b72e2 BF |
391 | |
392 | { | |
393 | # AUTOLOAD and DESTROY can be declared without a leading sub, | |
394 | # like BEGIN and friends. | |
395 | package NoSub; | |
396 | ||
397 | eval 'AUTOLOAD { our $AUTOLOAD; return $AUTOLOAD }'; | |
398 | ::ok( !$@, "AUTOLOAD without a leading sub is legal" ); | |
399 | ||
400 | eval "DESTROY { ::pass( q!DESTROY without a leading sub is legal and gets called! ) }"; | |
401 | { | |
402 | ::ok( NoSub->can("AUTOLOAD"), "...and sets up an AUTOLOAD normally" ); | |
403 | ::is( eval { NoSub->bluh }, "NoSub::bluh", "...which works as expected" ); | |
404 | } | |
405 | { bless {}, "NoSub"; } | |
406 | } | |
f226e9be FC |
407 | |
408 | eval { () = 3; new {} }; | |
409 | like $@, | |
410 | qr/^Can't call method "new" without a package or object reference/, | |
411 | 'Err msg from new{} when stack contains a number'; | |
412 | eval { () = "foo"; new {} }; | |
413 | like $@, | |
414 | qr/^Can't call method "new" without a package or object reference/, | |
415 | 'Err msg from new{} when stack contains a word'; | |
416 | eval { () = undef; new {} }; | |
417 | like $@, | |
418 | qr/^Can't call method "new" without a package or object reference/, | |
419 | 'Err msg from new{} when stack contains undef'; |