Affix

 view release on metacpan or  search on metacpan

lib/Affix/Wrap.pm  view on Meta::CPAN

package Affix::Wrap v1.0.9 {
    use v5.40;
    use feature 'class';
    no warnings 'experimental::class';
    no warnings 'experimental::builtin';
    use Path::Tiny;
    use Capture::Tiny qw[capture];
    use JSON::PP;
    use File::Basename qw[basename];
    use Affix          qw[];
    #
    class    #
        Affix::Wrap::Type {
        use Affix qw[Void];
        field $name : reader : param //= 'void';
        method to_string { $self->name }
        use overload '""' => 'to_string', fallback => 1;

        # Factory method to parse a C type string into objects
        sub parse ( $class, $t ) {
            return $class->new( name => 'void' ) unless defined $t;

            # Cleanup attributes and whitespace
            $t =~ s/__attribute__\s*\(\(.*\)\)//g;
            $t =~ s/^\s+|\s+$//g;

            # Function Pointer: Ret (*)(Args)
            if ( $t =~ /^(.+?)\s*\(\*\)\s*\((.*)\)$/ ) {
                my $ret_str  = $1;
                my $args_str = $2;
                my $ret      = $class->parse($ret_str);
                my @args;
                if ( $args_str ne '' && $args_str ne 'void' ) {
                    @args = map { $class->parse($_) } split( /\s*,\s*/, $args_str );
                }
                return Affix::Wrap::Type::CodeRef->new( ret => $ret, params => \@args );
            }
            if ( $t =~ /^(.*)\s*\[(\d+)\]$/ ) {
                return Affix::Wrap::Type::Array->new( of => $class->parse($1), count => $2 );
            }
            $t =~ s/(\*)\s*(?:const|restrict)\s*$/$1/;
            $t =~ /^(.+)\s*\*$/ ? Affix::Wrap::Type::Pointer->new( of => $class->parse($1) ) : $class->new( name => $t );
        }

        method affix_type {
            my $t = $self->name;
            $t =~ s/^(?:struct|union|enum)\s+//;
            $t =~ s/consts?\s+//g;
            $t =~ s/(\s+\**)const$/$1/g;
            $t =~ s/(\s+\**)restrict$/$1/g;
            $t =~ s/\s+$//;
            #
            state $type_map //= {
                void                 => 'Void',
                bool                 => 'Bool',
                short                => 'Short',
                'unsigned short'     => 'UShort',
                char                 => 'Char',
                'signed char'        => 'SChar',
                'unsigned char'      => 'UChar',
                int                  => 'Int',
                'unsigned int'       => 'UInt',
                long                 => 'Long',
                'unsigned long'      => 'ULong',
                'long long'          => 'LongLong',
                'unsigned long long' => 'ULongLong',
                float                => 'Float',
                double               => 'Double',

lib/Affix/Wrap.pm  view on Meta::CPAN

            [ map { $_->affix } @$args ]
        }
        } class    #
        Affix::Wrap::Driver::Clang {
        use Config;
        field $project_files : param : reader;
        field $allowed_files  = {};
        field $project_dirs   = [];
        field $paths_seen     = {};
        field $file_cache     = {};
        field $last_seen_file = undef;
        field $clang //= 'clang';
        method _basename ($path) { return '' unless defined $path; $path =~ s{^.*[/\\]}{}; return lc($path); }

        method _normalize ($path) {
            return '' unless defined $path && length $path;
            my $abs = Path::Tiny::path($path)->absolute->stringify;
            $abs =~ s{\\}{/}g;
            return $abs;
        }
        ADJUST {
            my %seen_dirs;
            for my $f (@$project_files) {
                next unless defined $f && length $f;
                my $abs = $self->_normalize($f);
                next unless length $abs;
                $allowed_files->{$abs} = 1;
                my $dir = Path::Tiny::path($abs)->parent->stringify;
                $dir =~ s{\\}{/}g;
                unless ( $seen_dirs{$dir}++ ) { push @$project_dirs, $dir; }
            }
        }

        method parse ( $entry_point, $include_dirs //= [] ) {
            if ( !defined $entry_point || !length $entry_point ) {
                ($entry_point) = grep { defined $_ && length $_ } @$project_files;
            }
            return () unless defined $entry_point && length $entry_point;
            my $ep_abs = $self->_normalize($entry_point);
            return () unless length $ep_abs;
            $allowed_files->{$ep_abs} = 1;
            $last_seen_file = $ep_abs;
            my $ep_dir = Path::Tiny::path($ep_abs)->parent->stringify;
            $ep_dir =~ s{\\}{/}g;
            my $found = 0;

            for my $pd (@$project_dirs) {
                if ( $ep_dir eq $pd ) { $found = 1; last; }
            }
            push @$project_dirs, $ep_dir unless $found;
            my @includes = map { "-I" . $self->_normalize($_) } @$include_dirs;
            for my $d (@$project_dirs) { push @includes, "-I$d"; }
            my @cmd = (
                $clang,                 '-target',         $self->_get_triple(),             '-Xclang',
                '-ast-dump=json',       '-Xclang',         '-detailed-preprocessing-record', '-fsyntax-only',
                '-fparse-all-comments', '-Wno-everything', @includes,                        $ep_abs
            );
            my ( $stdout, $stderr, $exit ) = Capture::Tiny::capture { system(@cmd); };
            if ( $exit != 0 )               { die "Clang Error:\n$stderr"; }
            if ( $stdout =~ /^.*?(\{.*)/s ) { $stdout = $1; }
            my $ast = JSON::PP::decode_json($stdout);
            my @objects;
            $self->_walk( $ast, \@objects, $ep_abs );
            $self->_scan_macros_fallback( \@objects );
            $self->_merge_typedefs( \@objects );
            $self->_wrap_named_types( \@objects );

            #~ @objects = sort { ( $a->file cmp $b->file ) || ( $a->start_offset <=> $b->start_offset ) } @objects;
            @objects;
        }

        method _walk( $node, $acc, $current_file ) {
            return unless ref $node eq 'HASH';
            my $kind      = $node->{kind} // 'Unknown';
            my $node_file = $self->_get_node_file($node);
            if ($node_file) {
                $current_file   = $self->_normalize($node_file);
                $last_seen_file = $current_file;
            }
            elsif ( defined $last_seen_file ) { $current_file = $last_seen_file; }
            if    ( $self->_is_valid_file($current_file) && !$node->{isImplicit} ) {
                if ( $kind eq 'MacroDefinitionRecord' ) {
                    if ( $node->{range} ) { $self->_macro( $node, $acc, $current_file ); }
                }
                elsif ( $kind eq 'TypedefDecl' ) { $self->_typedef( $node, $acc, $current_file ); }
                elsif ( $kind eq 'RecordDecl' || $kind eq 'CXXRecordDecl' ) {
                    $self->_record( $node, $acc, $current_file );
                    return;
                }
                elsif ( $kind eq 'EnumDecl' ) {
                    $self->_enum( $node, $acc, $current_file );
                    return;
                }
                elsif ( $kind eq 'VarDecl' ) {
                    if ( ( $node->{storageClass} // '' ) ne 'static' ) { $self->_var( $node, $acc, $current_file ); }
                }
                elsif ( $kind eq 'FunctionDecl' ) {
                    $self->_func( $node, $acc, $current_file );
                    return;
                }
                elsif ( $kind eq 'BuiltinType' ) { return; }
            }
            if ( $node->{inner} ) {
                for ( @{ $node->{inner} } ) { $self->_walk( $_, $acc, $current_file ); }
            }
        }

        method _is_valid_file ($f) {
            return 0 unless defined $f && length $f;
            return 0 if $f =~ m{^/usr/(include|lib|share|local/include)};
            return 0 if $f =~ m{^/System/Library};
            return 1 if $allowed_files->{$f};
            for my $dir (@$project_dirs) { return 1 if index( $f, $dir ) == 0; }
            return 0;
        }

        method _get_node_file($node) {
            my $loc = $node->{loc};
            return undef unless $loc;
            my $f;
            if ( ref($loc) eq 'HASH' ) {



( run in 1.148 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )