# New Ticket Created by Ron Blaschke
# Please include the string: [perl #44949]
# in the subject line of all future correspondence about this issue.
# <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=44949 >
Problem: I came across this when using GnuWin32's PCRE, where the
library is called F<pcre3.dll>, but F<pcre.pir> only considers
F<pcre.dll>.
When C<loadlib> fails it returns undef, which is not handled by
C<dlfunc>. You'll receive the following assertion error, for example
during F<t/library/pcre.t> and F<t/examples/library.t>.
# src\ops\core.ops:1194: failed assertion
'((&(interp)->ctx)->bp_ps.regs_p[-1L-(cur_opcode[2])])->pmc_ext'
This assertion is part of C<PMC_data($2)> in C<dlfunc>.
Solution: Attached patch adds a test to C<dlfunc> to check the library
argument. It also adds tests for C<loadlib> and C<dlfunc> with missing
libraries and functions. Finally, it adds checks to
F<runtime/parrot/library/pcre.pir> so it won't try to resolve functions
if the library can't be loaded, and adds F<pcre3.dll> to the Windows
candidate list.
$ runtests t\examples\library.t t\library\pcre.t t\pmc\nci.t
t\examples\library......ok
t\library\pcre..........ok
t\pmc\nci...............ok
All tests successful.
Files=3, Tests=69, 7 wallclock secs
Changed Files:
src/ops/core.ops
runtime/parrot/library/pcre.pir
t/pmc/nci.t
Ron
Index: src/ops/core.ops
===================================================================
--- src/ops/core.ops (revision 20855)
+++ src/ops/core.ops (working copy)
@@ -1191,8 +1191,11 @@
op dlfunc (out PMC, invar PMC, in STR, in STR) {
char * const name = string_to_cstring(interp, ($3));
- void *ptr = Parrot_dlsym(PMC_IS_NULL($2) ? NULL : PMC_data($2),
- name);
+ void *ptr = Parrot_dlsym(
+ PMC_IS_NULL($2) ? NULL :
+ $2->vtable->defined(interp, $2) ? PMC_data($2) :
+ NULL,
+ name);
funcptr_t p = D2FPTR(ptr);
@@ -1213,7 +1216,6 @@
op dlvar (out PMC, invar PMC, in STR) {
char * const name = string_to_cstring(interp, ($3));
-
void * const p = Parrot_dlsym(PMC_IS_NULL($2) ? NULL : PMC_data($2), name);
string_cstring_free(name);
if (p == NULL) {
Index: runtime/parrot/library/pcre.pir
===================================================================
--- runtime/parrot/library/pcre.pir (revision 20855)
+++ runtime/parrot/library/pcre.pir (working copy)
@@ -47,6 +47,7 @@
.local pmc pcre_function
.local pmc config
.local string osname
+ .local int loaded
config = _config()
osname = config['osname']
@@ -56,14 +57,26 @@
LIB_DEFAULT:
loadlib libpcre, 'libpcre'
- branch LIB_LOADED
+ loaded = defined libpcre
+ if loaded goto LIB_LOADED
+ branch LIB_RETURN
LIB_WIN32:
+ # Usually it's pcre.dll
loadlib libpcre, 'pcre'
- branch LIB_LOADED
+ loaded = defined libpcre
+ if loaded goto LIB_LOADED
+ # But maybe you have GnuWin32 pcre3.dll?
+ loadlib libpcre, 'pcre3'
+ loaded = defined libpcre
+ if loaded goto LIB_LOADED
+ branch LIB_RETURN
LIB_CYGWIN:
loadlib libpcre, 'cygpcre-0'
+ loaded = defined libpcre
+ if loaded goto LIB_LOADED
+ branch LIB_RETURN
LIB_LOADED:
store_global 'PCRE', 'lib', libpcre
@@ -88,6 +101,7 @@
dlfunc pcre_function, libpcre, 'pcre_copy_substring', 'itpiibi'
store_global 'PCRE::NCI', 'PCRE_copy_substring', pcre_function
+LIB_RETURN:
.return( libpcre )
.end
Index: t/pmc/nci.t
===================================================================
--- t/pmc/nci.t (revision 20855)
+++ t/pmc/nci.t (working copy)
@@ -6,7 +6,7 @@
use warnings;
use lib qw( . lib ../lib ../../lib );
use Test::More;
-use Parrot::Test tests => 61;
+use Parrot::Test tests => 64;
use Parrot::Config qw(%PConfig);
=head1 NAME
@@ -35,6 +35,86 @@
skip( "Please make libnci_test$PConfig{load_ext}",
Test::Builder->expected_tests() );
}
+ pir_output_is( << 'CODE', << 'OUTPUT', 'load library fails' );
+.sub test :main
+ .local pmc libnci_test
+ .local int lib_defined
+ libnci_test = loadlib "no_such_library"
+ lib_defined = defined libnci_test
+ if lib_defined goto LIB_DEFINED
+ branch LIB_UNDEFINED
+
+LIB_DEFINED:
+ print "defined\n"
+ branch LIB_END
+
+LIB_UNDEFINED:
+ print "undefined\n"
+ branch LIB_END
+
+LIB_END:
+.end
+CODE
+undefined
+OUTPUT
+
+ pir_output_is( << 'CODE', << 'OUTPUT', 'dlfunc on undef' );
+.sub test :main
+ .local pmc libnci_test
+ .local pmc func
+ .local int func_defined
+ libnci_test = loadlib "no_such_library"
+ dlfunc func, libnci_test, "no_such_function", "v"
+ func_defined = defined func
+ if func_defined goto FUNC_DEFINED
+ branch FUNC_UNDEFINED
+FUNC_DEFINED:
+ print "defined\n"
+ branch FUNC_END
+
+FUNC_UNDEFINED:
+ print "undefined\n"
+ branch FUNC_END
+
+FUNC_END:
+.end
+CODE
+undefined
+OUTPUT
+
+ pir_output_is( << 'CODE', << 'OUTPUT', 'dlfunc function not found' );
+.sub test :main
+ .local pmc libnci_test
+ .local pmc func
+ .local int func_defined
+ libnci_test = loadlib "libnci_test"
+ unless libnci_test goto NOT_LOADED
+ print "libnci_test was successfully loaded\n"
+
+ dlfunc func, libnci_test, "no_such_function", "v"
+ func_defined = defined func
+ if func_defined goto FUNC_DEFINED
+ branch FUNC_UNDEFINED
+
+FUNC_DEFINED:
+ print "defined\n"
+ branch FUNC_END
+
+FUNC_UNDEFINED:
+ print "undefined\n"
+ branch FUNC_END
+
+NOT_LOADED:
+ print "Could not load libnci_test\n"
+ branch FUNC_END
+
+FUNC_END:
+.end
+CODE
+libnci_test was successfully loaded
+undefined
+OUTPUT
+
pir_output_is( << 'CODE', << "OUTPUT", "nci_c - return a char in an
INTEGER register" );
.include "datatypes.pasm"