| # | |
| # Copyright 2002 Patrik Stridvall | |
| # | |
| # This library is free software; you can redistribute it and/or | |
| # modify it under the terms of the GNU Lesser General Public | |
| # License as published by the Free Software Foundation; either | |
| # version 2.1 of the License, or (at your option) any later version. | |
| # | |
| # This library is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| # Lesser General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU Lesser General Public | |
| # License along with this library; if not, write to the Free Software | |
| # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| # | |
| package c_type; | |
| use strict; | |
| use output qw($output); | |
| sub _refresh($); | |
| sub new($) | |
| { | |
| my ($proto) = @_; | |
| my $class = ref($proto) || $proto; | |
| my $self = {}; | |
| bless $self, $class; | |
| return $self; | |
| } | |
| # | |
| # Callback setters | |
| # | |
| sub set_find_align_callback($$) | |
| { | |
| my ($self, $find_align) = @_; | |
| $self->{FIND_ALIGN} = $find_align; | |
| } | |
| sub set_find_kind_callback($$) | |
| { | |
| my ($self, $find_kind) = @_; | |
| $self->{FIND_KIND} = $find_kind; | |
| } | |
| sub set_find_size_callback($$) | |
| { | |
| my ($self, $find_size) = @_; | |
| $self->{FIND_SIZE} = $find_size; | |
| } | |
| sub set_find_count_callback($$) | |
| { | |
| my ($self, $find_count) = @_; | |
| $self->{FIND_COUNT} = $find_count; | |
| } | |
| # | |
| # Property setter / getter functions (each does both) | |
| # | |
| sub kind($;$) | |
| { | |
| my ($self, $kind) = @_; | |
| if (defined $kind) | |
| { | |
| $self->{KIND} = $kind; | |
| $self->{DIRTY} = 1; | |
| } | |
| $self->_refresh() if (!defined $self->{KIND}); | |
| return $self->{KIND}; | |
| } | |
| sub _name($;$) | |
| { | |
| my ($self, $_name) = @_; | |
| if (defined $_name) | |
| { | |
| $self->{_NAME} = $_name; | |
| $self->{DIRTY} = 1; | |
| } | |
| return $self->{_NAME}; | |
| } | |
| sub name($;$) | |
| { | |
| my ($self, $name) = @_; | |
| if (defined $name) | |
| { | |
| $self->{NAME} = $name; | |
| $self->{DIRTY} = 1; | |
| } | |
| return $self->{NAME} if ($self->{NAME}); | |
| return "$self->{KIND} $self->{_NAME}"; | |
| } | |
| sub pack($;$) | |
| { | |
| my ($self, $pack) = @_; | |
| if (defined $pack) | |
| { | |
| $self->{PACK} = $pack; | |
| $self->{DIRTY} = 1; | |
| } | |
| return $self->{PACK}; | |
| } | |
| sub align($) | |
| { | |
| my ($self) = @_; | |
| $self->_refresh(); | |
| return $self->{ALIGN}; | |
| } | |
| sub fields($) | |
| { | |
| my ($self) = @_; | |
| my $count = $self->field_count; | |
| my @fields = (); | |
| for (my $n = 0; $n < $count; $n++) { | |
| my $field = 'c_type_field'->new($self, $n); | |
| push @fields, $field; | |
| } | |
| return @fields; | |
| } | |
| sub field_base_sizes($) | |
| { | |
| my ($self) = @_; | |
| $self->_refresh(); | |
| return $self->{FIELD_BASE_SIZES}; | |
| } | |
| sub field_aligns($) | |
| { | |
| my ($self) = @_; | |
| $self->_refresh(); | |
| return $self->{FIELD_ALIGNS}; | |
| } | |
| sub field_count($) | |
| { | |
| my ($self) = @_; | |
| return scalar @{$self->{FIELD_TYPE_NAMES}}; | |
| } | |
| sub field_names($;$) | |
| { | |
| my ($self, $field_names) = @_; | |
| if (defined $field_names) | |
| { | |
| $self->{FIELD_NAMES} = $field_names; | |
| $self->{DIRTY} = 1; | |
| } | |
| return $self->{FIELD_NAMES}; | |
| } | |
| sub field_offsets($) | |
| { | |
| my ($self) = @_; | |
| $self->_refresh(); | |
| return $self->{FIELD_OFFSETS}; | |
| } | |
| sub field_sizes($) | |
| { | |
| my ($self) = @_; | |
| $self->_refresh(); | |
| return $self->{FIELD_SIZES}; | |
| } | |
| sub field_type_names($;$) | |
| { | |
| my ($self, $field_type_names) = @_; | |
| if (defined $field_type_names) | |
| { | |
| $self->{FIELD_TYPE_NAMES} = $field_type_names; | |
| $self->{DIRTY} = 1; | |
| } | |
| return $self->{FIELD_TYPE_NAMES}; | |
| } | |
| sub size($) | |
| { | |
| my ($self) = @_; | |
| $self->_refresh(); | |
| return $self->{SIZE}; | |
| } | |
| sub _refresh($) | |
| { | |
| my ($self) = @_; | |
| return if (!$self->{DIRTY}); | |
| my $pack = $self->pack; | |
| $pack = 8 if !defined($pack); | |
| my $max_field_align = 0; | |
| my $offset = 0; | |
| my $bitfield_size = 0; | |
| my $bitfield_bits = 0; | |
| my $n = 0; | |
| foreach my $field ($self->fields()) | |
| { | |
| my $type_name = $field->type_name; | |
| my $bits; | |
| my $count; | |
| if ($type_name =~ s/^(.*?)\s*(?:\[\s*(.*?)\s*\]|:(\d+))?$/$1/) | |
| { | |
| $count = $2; | |
| $bits = $3; | |
| } | |
| my $declspec_align; | |
| if ($type_name =~ s/\s+DECLSPEC_ALIGN\((\d+)\)//) | |
| { | |
| $declspec_align=$1; | |
| } | |
| my $base_size = $self->{FIND_SIZE}($type_name); | |
| my $type_size=$base_size; | |
| if (defined $count) | |
| { | |
| $count=$self->{FIND_COUNT}($count) if ($count !~ /^\d+$/); | |
| if (!defined $count) | |
| { | |
| $type_size=undef; | |
| } | |
| else | |
| { | |
| print STDERR "$type_name -> type_size=undef, count=$count\n" if (!defined $type_size); | |
| $type_size *= int($count); | |
| } | |
| } | |
| if ($bitfield_size != 0) | |
| { | |
| if (($type_name eq "" and defined $bits and $bits == 0) or | |
| (defined $type_size and $bitfield_size != $type_size) or | |
| !defined $bits or | |
| $bitfield_bits + $bits > 8 * $bitfield_size) | |
| { | |
| # This marks the end of the previous bitfield | |
| $bitfield_size=0; | |
| $bitfield_bits=0; | |
| } | |
| else | |
| { | |
| $bitfield_bits+=$bits; | |
| $n++; | |
| next; | |
| } | |
| } | |
| $self->{ALIGN} = $self->{FIND_ALIGN}($type_name); | |
| $self->{ALIGN} = $declspec_align if (defined $declspec_align); | |
| if (defined $self->{ALIGN}) | |
| { | |
| $self->{ALIGN} = $pack if ($self->{ALIGN} > $pack); | |
| $max_field_align = $self->{ALIGN} if ($self->{ALIGN}) > $max_field_align; | |
| if ($offset % $self->{ALIGN} != 0) { | |
| $offset = (int($offset / $self->{ALIGN}) + 1) * $self->{ALIGN}; | |
| } | |
| } | |
| if ($self->{KIND} !~ /^(?:struct|union)$/) | |
| { | |
| $self->{KIND} = $self->{FIND_KIND}($type_name) || ""; | |
| } | |
| if (!$type_size) | |
| { | |
| $self->{ALIGN} = undef; | |
| $self->{SIZE} = undef; | |
| return; | |
| } | |
| $self->{FIELD_ALIGNS}->[$n] = $self->{ALIGN}; | |
| $self->{FIELD_BASE_SIZES}->[$n] = $base_size; | |
| $self->{FIELD_OFFSETS}->[$n] = $offset; | |
| $self->{FIELD_SIZES}->[$n] = $type_size; | |
| $offset += $type_size; | |
| if ($bits) | |
| { | |
| $bitfield_size=$type_size; | |
| $bitfield_bits=$bits; | |
| } | |
| $n++; | |
| } | |
| $self->{ALIGN} = $pack; | |
| $self->{ALIGN} = $max_field_align if ($max_field_align < $pack); | |
| $self->{SIZE} = $offset; | |
| if ($self->{KIND} =~ /^(?:struct|union)$/) { | |
| if ($self->{SIZE} % $self->{ALIGN} != 0) { | |
| $self->{SIZE} = (int($self->{SIZE} / $self->{ALIGN}) + 1) * $self->{ALIGN}; | |
| } | |
| } | |
| $self->{DIRTY} = 0; | |
| } | |
| package c_type_field; | |
| sub new($$$) | |
| { | |
| my ($proto, $type, $number) = @_; | |
| my $class = ref($proto) || $proto; | |
| my $self = {TYPE=> $type, | |
| NUMBER => $number | |
| }; | |
| bless $self, $class; | |
| return $self; | |
| } | |
| sub align($) | |
| { | |
| my ($self) = @_; | |
| return $self->{TYPE}->field_aligns()->[$self->{NUMBER}]; | |
| } | |
| sub base_size($) | |
| { | |
| my ($self) = @_; | |
| return $self->{TYPE}->field_base_sizes()->[$self->{NUMBER}]; | |
| } | |
| sub name($) | |
| { | |
| my ($self) = @_; | |
| return $self->{TYPE}->field_names()->[$self->{NUMBER}]; | |
| } | |
| sub offset($) | |
| { | |
| my ($self) = @_; | |
| return $self->{TYPE}->field_offsets()->[$self->{NUMBER}]; | |
| } | |
| sub size($) | |
| { | |
| my ($self) = @_; | |
| return $self->{TYPE}->field_sizes()->[$self->{NUMBER}]; | |
| } | |
| sub type_name($) | |
| { | |
| my ($self) = @_; | |
| return $self->{TYPE}->field_type_names()->[$self->{NUMBER}]; | |
| } | |
| 1; |