# New Ticket Created by
# Please include the string: [perl #127460]
# in the subject line of all future correspondence about this issue.
# <URL: https://rt.perl.org/Ticket/Display.html?id=127460 >
Setting an int32 member of a CUnion (nested in a CStruct) to 0 from C
causes the NativeCall layer to return a type object to the user.
This is a bit bizarre, but the files are below. I'm nesting a CUnion
within a CStruct in order to match the layout of the C struct.
-- bug.pm6 --
use NativeCall;
constant ZERO = 0;
constant TYPE_BOOL = 2;
class Inline::Scheme::Guile::AltType is repr('CUnion') {
has int32 $.int_content; # When this is populated with 0,
NativeCall returns the I::S::G::AltType type rather than an instance of
the object.
has Str $.string_content;
}
class Inline::Scheme::Guile::ConsCell is repr('CStruct') {
has int32 $.type;
HAS Inline::Scheme::Guile::AltType $.content; # The nested
CUnion is here.
}
class Inline::Scheme::Guile {
sub native(Sub $sub) {
my Str $path = %?RESOURCES<libraries/guile-helper>.Str;
die "unable to find libguile-helper library"
unless $path;
trait_mod:<is>($sub, :native($path));
}
sub run( Str $expression,
&marshal_guile
(Pointer[Inline::Scheme::Guile::ConsCell]) )
{ ... }
native(&run);
method run( Str $expression ) {
my @stuff;
my $ref = sub ( Pointer[Inline::Scheme::Guile::ConsCell]
$cell ) {
CATCH {
warn "Don't die in callback, warn
instead.\n";
warn $_;
}
my $type = $cell.deref.type;
given $type {
when TYPE_BOOL {
my $content =
$cell.deref.content; # Content comes back as the AltType type object
rather than an instance.
if $content.int_content == 1 {
@stuff.push( True );
}
else {
@stuff.push( False );
}
}
}
}
run( $expression, $ref );
return @stuff;
}
}
--cut here--
-- bug.c --
#include <libguile.h>
#include <stdio.h>
typedef enum {
VOID = -1,
ZERO = 0,
TYPE_BOOL = 2,
}
cons_cell_type;
typedef struct {
cons_cell_type type;
union {
long int_content; // This is the problem.
char* string_content;
};
}
cons_cell;
static void _walk_scm( SCM scm, cons_cell* result ) {
int num_values = scm_c_nvalues( scm );
// '#f' is not null, bool, false and only 1 value.
//
if ( num_values == 1 &&
//scm_is_null( scm ) &&
scm_is_bool( scm ) &&
scm_is_false( scm ) ) {
result[0].type = TYPE_BOOL;
// result[0].int_content = -1; // Assigning -1 to
int_content returns an instance
result[0].int_content = 0; // Assigning 0 to int_content
returns the type object.
result[1].type = ZERO;
return;
}
// '#t' is not null, bool, not false, true and only 1 value.
//
if ( num_values == 1 &&
//scm_is_null( scm ) &&
scm_is_bool( scm ) &&
!scm_is_false( scm ) &&
scm_is_true( scm ) ) {
result[0].type = TYPE_BOOL;
result[0].int_content = 1;
result[1].type = ZERO;
return;
}
}
void* _run( void* expression ) {
SCM str = scm_from_latin1_string( (char*) expression );
SCM scm = scm_eval_string( str );
// Sigh, special-case void lists.
if ( scm_c_nvalues( scm ) == 0 ) {
cons_cell* result = malloc( sizeof( cons_cell ) * 2 );
result[0].type = VOID;
result[1].type = ZERO;
return result;
}
cons_cell* result = malloc( sizeof( cons_cell ) * 2 );
_walk_scm( scm, result );
return result;
}
void run( const char* expression, void (*unmarshal(void*)) ) {
cons_cell* cells = scm_with_guile( _run, (void*)expression );
cons_cell* head = cells;
while( head->type != ZERO ) {
unmarshal(head++);
}
free(cells);
}
--cut here--
-- t/00-core.t --
#!/usr/bin/env perl6
use v6;
use Test;
use NativeCall;
plan 3;
use Inline::Scheme::Guile;
my $g = Inline::Scheme::Guile.new;
is-deeply [ $g.run( q{#f} ) ], [ False ], q{value (#f)}; # This should
segfault when 0 is used as False rather than -1.
is-deeply [ $g.run( q{#t} ) ], [ True ], q{value (#t)}; # This should
work regardless.
--cut here--