# 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"

Reply via email to