Fixed ordinal to string conversion for enumerations

* clean up actual ordinal to string conversion in system unit: try to use records instead of hardcoded offsets
* before emitting the enum ordinal to string rtti information for enums, they need to be sorted according to their values first. Otherwise rtti information for sparse enums is broken.

git-svn-id: trunk@16218 -
This commit is contained in:
tom_at_work 2010-10-24 21:35:16 +00:00
parent e82d25d211
commit f520989064
2 changed files with 111 additions and 36 deletions

View File

@ -1082,7 +1082,7 @@ implementation
var var
t:Tenumsym; t:Tenumsym;
syms:Penumsym; syms:Penumsym;
sym_count,sym_alloc:longint; sym_count,sym_alloc:sizeuint;
offsets:^longint; offsets:^longint;
h,i,p,o,st:longint; h,i,p,o,st:longint;
begin begin
@ -1139,6 +1139,32 @@ implementation
end; end;
st:=enumdef_rtti_calcstringtablestart(def); st:=enumdef_rtti_calcstringtablestart(def);
enumdef_rtti_string2ordindex(sym_count,offsets,syms,st); enumdef_rtti_string2ordindex(sym_count,offsets,syms,st);
{ Sort the syms by enum value }
if sym_count>=2 then
begin
p:=1;
while 2*p<sym_count do
p:=2*p;
while p<>0 do
begin
for h:=p to sym_count-1 do
begin
i:=h;
t:=syms[i];
o:=offsets[i];
repeat
if syms[i-p].value<=t.value then
break;
syms[i]:=syms[i-p];
offsets[i]:=offsets[i-p];
dec(i,p);
until i<p;
syms[i]:=t;
offsets[i]:=o;
end;
p:=p shr 1;
end;
end;
enumdef_rtti_ord2stringindex(sym_count,offsets,syms,st); enumdef_rtti_ord2stringindex(sym_count,offsets,syms,st);
freemem(syms); freemem(syms);
freemem(offsets); freemem(offsets);

View File

@ -409,71 +409,120 @@ end;
function fpc_shortstr_enum_intern(ordinal,len:sizeint;typinfo,ord2strindex:pointer;out s:shortstring): longint; function fpc_shortstr_enum_intern(ordinal,len:sizeint;typinfo,ord2strindex:pointer;out s:shortstring): longint;
{ Enumeration RTTI has the following format (given by typinfo parameter):
Tenum_rtti_header // variable sized; shortstring only contains minimum amount of data, e.g. length + string
(alignment) // if FPC_REQUIRES_PROPER_ALIGNMENT there is an alignment to pointer size
Tenum_rtti_body // more RTTI information
}
type type
Ptypeinfo=^Ttypeinfo; PPstring=^Pstring;
Ttypeinfo=record
Penum_rtti_header=^Tenum_rtti_header;
Tenum_rtti_header=record
kind:byte; kind:byte;
name:shortstring; num_chars:byte;
chars:array[0..0] of char; // variable length with size of num_chars;
end; end;
Penuminfo=^Tenuminfo; Penum_rtti_body=^Tenum_rtti_body;
Tenuminfo={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record Tenum_rtti_body={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record
ordtype:byte; ordtype:byte;
minvalue,maxvalue:longint; minvalue,maxvalue:longint;
basetype:pointer; basetype:pointer;
namelist:shortstring; { more data here, but not needed }
end; end;
Tsorted_array={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record Psearch_data=^Tsearch_data;
o:longint; Tsearch_data={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record
s:Pstring; value:longint;
name:Pstring;
end;
Penum_ord_to_string_header=^Tenum_ord_to_string_header;
Tenum_ord_to_string_header={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record
o:(lookup,search);
end;
Penum_ord_to_string_lookup=^Tenum_ord_to_string_lookup;
Tenum_ord_to_string_lookup={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record
header:Tenum_ord_to_string_header;
lookup_data:array[0..0] of Pstring;
end;
Penum_ord_to_string_search=^Tenum_ord_to_string_search;
Tenum_ord_to_string_search={$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record
header:Tenum_ord_to_string_header;
num_entries:longint; // only if o == 0
search_data:array[0..0] of Tsearch_data;
end;
function align_up(value:ptruint; alignment:sizeint) : ptruint;
begin
align_up:=(value + (alignment - 1)) and not (alignment - 1);
end; end;
var var
p:Pstring; p:Pstring;
l,h,m:cardinal;
sorted_array:^Tsorted_array; enum_o2s : Penum_ord_to_string_header;
i,spaces:byte; header:Penum_rtti_header;
body:Penum_rtti_body;
res:Pshortstring;
sorted_data:Psearch_data;
spaces,i,m,h,l:longint;
begin begin
{ set default return value }
fpc_shortstr_enum_intern:=107; fpc_shortstr_enum_intern:=107;
if Pcardinal(ord2strindex)^=0 then
enum_o2s:=Penum_ord_to_string_header(ord2strindex);
{ depending on the type of table in ord2strindex retrieve the data }
if (enum_o2s^.o=lookup) then
begin begin
{The compiler did generate a lookup table.} { direct lookup table }
with Penuminfo(Pbyte(typinfo)+2+length(Ptypeinfo(typinfo)^.name))^ do header:=Penum_rtti_header(typinfo);
{ calculate address of enum rtti body: add the actual size of the
enum_rtti_header, and then align. Use an alignment of 1 (which
does nothing) in case FPC_REQUIRES_PROPER_ALIGNMENT is not set
to avoid the need for an if in this situation }
body:=Penum_rtti_body(align_up(ptruint(header) + 2 * sizeof(byte) { kind, num_chars } + header^.num_chars,
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT} 1 {$else} sizeof(pointer) {$endif}));
with (body^) do
begin begin
{ Bounds check for the ordinal value for this enum }
if (ordinal<minvalue) or (ordinal>maxvalue) then if (ordinal<minvalue) or (ordinal>maxvalue) then
exit; {Invalid ordinal value for this enum.} exit;
{ make the ordinal index for lookup zero-based }
dec(ordinal,minvalue); dec(ordinal,minvalue);
end; end;
{Get the address of the string.} res:=Penum_ord_to_string_lookup(enum_o2s)^.lookup_data[ordinal];
p:=Pshortstring((PPpointer(ord2strindex+sizeof(longint))+ordinal)^); if (not assigned(res)) then
if p=nil then exit;
exit; {Invalid ordinal value for this enum.} s:=res^;
s:=p^;
end end
else else
begin begin
{The compiler did generate a sorted array of (ordvalue,Pstring) tuples.} { The compiler did generate a sorted array of (ordvalue,Pstring) tuples }
sorted_array:=pointer(Pcardinal(ord2strindex)+2); sorted_data:=Penum_ord_to_string_search(enum_o2s)^.search_data;
{Use a binary search to get the string.} { Use a binary search to get the string }
l:=0; l:=0;
h:=(Pcardinal(ord2strindex)+1)^-1; h:=Penum_ord_to_string_search(enum_o2s)^.num_entries-1;
repeat repeat
m:=(l+h) div 2; m:=(l+h) div 2;
if ordinal>sorted_array[m].o then if ordinal>sorted_data[m].value then
l:=m+1 l:=m+1
else if ordinal<sorted_array[m].o then else if ordinal<sorted_data[m].value then
h:=m-1 h:=m-1
else else
break; break;
if l>h then if l>h then
exit; {Ordinal value not found? Kaboom.} exit; { Ordinal value not found? Exit }
until false; until false;
s:=sorted_array[m].s^; s:=sorted_data[m].name^;
end; end;
{Pad the string with spaces if necessary.}
if len>length(s) then { Pad the string with spaces if necessary }
if (len>length(s)) then
begin begin
spaces:=len-length(s); spaces:=len-length(s);
for i:=1 to spaces do for i:=1 to spaces do