Skip to content

Commit 053d33f

Browse files
committed
Add support for Ada 2022's Big_Integer and Big_Real
TN: UA19-042 Change-Id: If877215b73663e73cbb399b49702edb347dce08b
1 parent c81db28 commit 053d33f

File tree

8 files changed

+210
-0
lines changed

8 files changed

+210
-0
lines changed

gnatdbg/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import gdb.printing
33

44

5+
from gnatdbg.big_numbers import BigIntegerPrinter, BigRealPrinter
56
from gnatdbg.lists import (
67
DoublyLinkedListPrinter, DoublyLinkedListCursorPrinter,
78
)
@@ -31,6 +32,9 @@ def create_printers(name='gnat-runtime'):
3132
printers = GDBPrettyPrinters(name)
3233

3334
for printer in [
35+
BigIntegerPrinter,
36+
BigRealPrinter,
37+
3438
DoublyLinkedListPrinter,
3539
DoublyLinkedListCursorPrinter,
3640

gnatdbg/big_numbers.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"""
2+
Pretty-printers and helpers for big numbers (Ada.Numerics.Big_Numbers).
3+
"""
4+
5+
import gdb
6+
7+
8+
from gnatdbg.printers import PrettyPrinter
9+
10+
11+
class BigInteger:
12+
"""
13+
Helper class to inspect Big_Integer values.
14+
"""
15+
16+
def __init__(self, value):
17+
self.value = value
18+
19+
@property
20+
def _bignum_address(self):
21+
return self.value["value"]["c"]
22+
23+
@property
24+
def is_valid(self):
25+
"""
26+
Return whether this big integer is valid.
27+
"""
28+
return bool(self._bignum_address)
29+
30+
def get(self):
31+
# The data structure used to store big numbers is
32+
# System.Shared_Bignums.Bignum_Data. Unfortunately, in most cases we
33+
# don't have debug info for this type, so we have to poke blindly at
34+
# memory.
35+
#
36+
# We consider that big number data is layed out as an array of uint32_t
37+
# values. The first one gives the number N of 32-bit digits that make
38+
# up this number (24 least significant bits) and whether that number is
39+
# negative (25th bit). Then the next N uint32_t values are the 32-bit
40+
# digits for the big number (most significant digits first).
41+
#
42+
# TODO: it is not clear reading the spec how the first digit is mapped
43+
# on big-endian systems.
44+
uint32_t = gdb.lookup_type('uint32_t')
45+
data_ptr = self._bignum_address.cast(uint32_t.pointer())
46+
47+
info = data_ptr.dereference()
48+
length = info & 0xfff
49+
neg = bool(info >> 24)
50+
51+
array_ptr_type = uint32_t.array(length).pointer()
52+
data_array = data_ptr.cast(array_ptr_type).dereference()
53+
54+
result = 0
55+
for i in range(length):
56+
result = result << 32 | int(data_array[i + 1])
57+
58+
if neg:
59+
result = -result
60+
61+
return result
62+
63+
64+
class BigReal:
65+
"""
66+
Helper class to inspect Big_Real values.
67+
"""
68+
69+
def __init__(self, value):
70+
self.value = value
71+
self.numerator = BigInteger(value["num"])
72+
self.denominator = BigInteger(value["den"])
73+
74+
@property
75+
def is_valid(self):
76+
return self.numerator.is_valid and self.denominator.is_valid
77+
78+
def get_numerator(self):
79+
return self.numerator.get()
80+
81+
def get_denominator(self):
82+
return self.denominator.get()
83+
84+
85+
class BigIntegerPrinter(PrettyPrinter):
86+
"""Pretty-print Big_Integer values."""
87+
88+
name = 'Big_Integer'
89+
type_pretty_name = 'ada.numerics.big_numbers.big_integers.big_integer'
90+
91+
def to_string(self):
92+
val = BigInteger(self.value)
93+
value = None
94+
try:
95+
if val.is_valid:
96+
value = val.get()
97+
except gdb.MemoryError:
98+
value_repr = '[Invalid]'
99+
else:
100+
value_repr = '[Uninitialized]' if value is None else str(value)
101+
102+
return f'{self.name} ({value_repr})'
103+
104+
105+
class BigRealPrinter(PrettyPrinter):
106+
"""Pretty-print Big_Real values."""
107+
108+
name = 'Big_Real'
109+
type_pretty_name = 'ada.numerics.big_numbers.big_reals.big_real'
110+
111+
def to_string(self):
112+
val = BigReal(self.value)
113+
try:
114+
value_repr = (
115+
f'{val.get_numerator()} / {val.get_denominator()}'
116+
if val.is_valid
117+
else '[Uninitialized]'
118+
)
119+
except gdb.MemoryError:
120+
value_repr = '[Invalid]'
121+
return f'{self.name} ({value_repr})'

testsuite/tests/big-integers/foo.adb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
pragma Ada_2022;
2+
3+
with Ada.Numerics.Big_Numbers.Big_Integers;
4+
use Ada.Numerics.Big_Numbers.Big_Integers;
5+
6+
procedure Foo is
7+
Uninit, Zero : Big_Integer;
8+
Neg_One, Neg_Small, Neg_Big : Big_Integer;
9+
Pos_One, Pos_Small, Pos_Big : Big_Integer;
10+
Corrupted : Big_Integer;
11+
begin
12+
Zero := 0;
13+
Neg_One := -1;
14+
Neg_Small := -1000;
15+
Neg_Big := -1234567890_0987654321_1234567890_0987654321;
16+
Pos_One := 1;
17+
Pos_Small := 1000;
18+
Pos_Big := 1234567890_0987654321_1234567890_0987654321;
19+
Corrupted := Zero; -- BREAK
20+
end Foo;

testsuite/tests/big-integers/test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from support.build import gnatmake
2+
from support.gdb import GDBSession
3+
4+
5+
gnatmake('foo')
6+
gdb = GDBSession('foo')
7+
gdb.run_to(gdb.find_loc('foo.adb', 'BREAK'))
8+
9+
gdb.execute('set corrupted.value.c := 0x1')
10+
11+
gdb.print_expr('uninit', 'Big_Integer ([Uninitialized])')
12+
gdb.print_expr('zero', 'Big_Integer (0)')
13+
gdb.print_expr('neg_one', 'Big_Integer (-1)')
14+
gdb.print_expr('neg_small', 'Big_Integer (-1000)')
15+
gdb.print_expr(
16+
'neg_big',
17+
'Big_Integer (-1234567890098765432112345678900987654321)'
18+
)
19+
gdb.print_expr('pos_one', 'Big_Integer (1)')
20+
gdb.print_expr('pos_small', 'Big_Integer (1000)')
21+
gdb.print_expr(
22+
'pos_big',
23+
'Big_Integer (1234567890098765432112345678900987654321)'
24+
)
25+
gdb.print_expr('corrupted', 'Big_Integer ([Invalid])')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
driver: python

testsuite/tests/big-reals/foo.adb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
pragma Ada_2022;
2+
3+
with Ada.Numerics.Big_Numbers.Big_Integers;
4+
use Ada.Numerics.Big_Numbers.Big_Integers;
5+
with Ada.Numerics.Big_Numbers.Big_Reals;
6+
use Ada.Numerics.Big_Numbers.Big_Reals;
7+
8+
procedure Foo is
9+
Uninit, Zero : Big_Real;
10+
Neg_One, Neg_Small : Big_Real;
11+
Pos_One, Pos_Small : Big_Real;
12+
Corrupted : Big_Real;
13+
begin
14+
Zero := 0 / 5;
15+
Neg_One := -1 / 1;
16+
Neg_Small := 10 / (-9);
17+
Pos_One := 6 / 6;
18+
Pos_Small := 1000 / 3;
19+
Corrupted := Pos_One;
20+
Corrupted := Zero; -- BREAK
21+
end Foo;

testsuite/tests/big-reals/test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from support.build import gnatmake
2+
from support.gdb import GDBSession
3+
4+
5+
gnatmake('foo')
6+
gdb = GDBSession('foo')
7+
gdb.run_to(gdb.find_loc('foo.adb', 'BREAK'))
8+
9+
gdb.execute('set corrupted.num.value.c := 0x1')
10+
11+
gdb.print_expr('uninit', 'Big_Real ([Uninitialized])')
12+
gdb.print_expr('zero', 'Big_Real (0 / 1)')
13+
gdb.print_expr('neg_one', 'Big_Real (-1 / 1)')
14+
gdb.print_expr('neg_small', 'Big_Real (-10 / 9)')
15+
gdb.print_expr('pos_one', 'Big_Real (1 / 1)')
16+
gdb.print_expr('pos_small', 'Big_Real (1000 / 3)')
17+
gdb.print_expr('corrupted', 'Big_Real ([Invalid])')

testsuite/tests/big-reals/test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
driver: python

0 commit comments

Comments
 (0)