1
1
from __future__ import annotations
2
2
3
3
from dataclasses import dataclass
4
- from typing import TYPE_CHECKING , NamedTuple
4
+ from typing import TYPE_CHECKING
5
5
6
6
from pyk .kast .inner import KApply
7
- from pyk .kast .prelude .bytes import bytesToken
8
- from pyk .kast .prelude .kint import intToken
9
-
10
- from .alloc import Allocation , AllocInfo , Memory , ProvenanceMap
11
- from .ty import ArrayT , Bool , EnumT , Int , IntTy , Uint
12
- from .value import AggregateValue , BoolValue , IntValue , RangeValue , Value
7
+ from pyk .kast .prelude .string import stringToken
8
+
9
+ from .alloc import Allocation , AllocInfo , Memory , ProvenanceEntry , ProvenanceMap
10
+ from .ty import ArrayT , Bool , EnumT , Int , IntTy , PtrT , RefT , Str , Uint
11
+ from .value import (
12
+ NO_METADATA ,
13
+ AggregateValue ,
14
+ AllocRefValue ,
15
+ BoolValue ,
16
+ DynamicSize ,
17
+ IntValue ,
18
+ RangeValue ,
19
+ StaticSize ,
20
+ StrValue ,
21
+ Value ,
22
+ )
13
23
14
24
if TYPE_CHECKING :
15
25
from collections .abc import Mapping
16
26
17
27
from pyk .kast import KInner
18
28
19
- from .alloc import AllocId
20
29
from .ty import Ty , TypeMetadata , UintTy
30
+ from .value import Metadata
21
31
22
32
23
33
@dataclass
24
34
class UnableToDecodeValue (Value ):
25
- data : bytes
26
- type_info : TypeMetadata
35
+ msg : str
27
36
28
37
def to_kast (self ) -> KInner :
29
38
return KApply (
30
- 'Evaluation::UnableToDecodeValue' ,
31
- bytesToken (self .data ),
32
- KApply ('TypeInfo::VoidType' ), # TODO: TypeInfo -> KAST transformation
39
+ 'Evaluation::UnableToDecodePy' ,
40
+ stringToken (self .msg ),
33
41
)
34
42
35
43
36
- @dataclass
37
- class UnableToDecodeAlloc (Value ):
38
- data : bytes
39
- ty : Ty
40
-
41
- def to_kast (self ) -> KInner :
42
- return KApply (
43
- 'Evaluation::UnableToDecodeAlloc' ,
44
- bytesToken (self .data ),
45
- KApply ('ty' , intToken (self .ty )),
46
- KApply ('ProvenanceMapEntries::empty' ), # TODO
47
- )
48
-
49
-
50
- class ProvenanceMapEntry (NamedTuple ):
51
- offset : int
52
- alloc_id : AllocId
53
-
54
-
55
44
def decode_alloc_or_unable (alloc_info : AllocInfo , types : Mapping [Ty , TypeMetadata ]) -> Value :
56
45
match alloc_info :
57
46
case AllocInfo (
@@ -66,27 +55,81 @@ def decode_alloc_or_unable(alloc_info: AllocInfo, types: Mapping[Ty, TypeMetadat
66
55
),
67
56
):
68
57
data = bytes (n or 0 for n in bytez )
58
+ return _decode_memory_alloc_or_unable (data = data , ptrs = ptrs , ty = ty , types = types )
59
+ case _:
60
+ raise AssertionError ('Unhandled case' )
61
+
69
62
70
- if not ptrs : # TODO generalize to lists with at most one entry
71
- type_info = types [ty ]
72
- return decode_value_or_unable (data = data , type_info = type_info , types = types )
63
+ def _decode_memory_alloc_or_unable (
64
+ data : bytes ,
65
+ ptrs : list [ProvenanceEntry ],
66
+ ty : Ty ,
67
+ types : Mapping [Ty , TypeMetadata ],
68
+ ) -> Value :
69
+ try :
70
+ type_info = types [ty ]
71
+ except KeyError :
72
+ return UnableToDecodeValue (f'Unknown type: { ty } ' )
73
73
74
- return UnableToDecodeAlloc (data = data , ty = ty )
74
+ match ptrs :
75
+ case []:
76
+ return decode_value_or_unable (data = data , type_info = type_info , types = types )
77
+
78
+ case [ProvenanceEntry (0 , alloc_id )]:
79
+ if (pointee_ty := _pointee_ty (type_info )) is not None : # ensures this is a reference type
80
+ try :
81
+ pointee_type_info = types [pointee_ty ]
82
+ except KeyError :
83
+ return UnableToDecodeValue (f'Unknown pointee type: { pointee_ty } ' )
84
+
85
+ metadata = _metadata (pointee_type_info )
86
+
87
+ if len (data ) == 8 :
88
+ # single slim pointer (assumes usize == u64)
89
+ return AllocRefValue (alloc_id = alloc_id , metadata = metadata )
90
+
91
+ if len (data ) == 16 and metadata == DynamicSize (1 ):
92
+ # sufficient data to decode dynamic size (assumes usize == u64)
93
+ # expect fat pointer
94
+ return AllocRefValue (
95
+ alloc_id = alloc_id ,
96
+ metadata = DynamicSize (int .from_bytes (data [8 :16 ], byteorder = 'little' , signed = False )),
97
+ )
98
+
99
+ return UnableToDecodeValue (f'Unable to decode alloc: { data !r} , of type: { type_info } ' )
100
+
101
+
102
+ def _pointee_ty (type_info : TypeMetadata ) -> Ty | None :
103
+ match type_info :
104
+ case PtrT (ty ) | RefT (ty ):
105
+ return ty
75
106
case _:
76
- raise AssertionError ('Unhandled case' )
107
+ return None
108
+
109
+
110
+ def _metadata (type_info : TypeMetadata ) -> Metadata :
111
+ match type_info :
112
+ case ArrayT (length = None ):
113
+ return DynamicSize (1 ) # 1 is a placeholder, the actual size is inferred from the slice data
114
+ case ArrayT (length = int () as length ):
115
+ return StaticSize (length )
116
+ case _:
117
+ return NO_METADATA
77
118
78
119
79
120
def decode_value_or_unable (data : bytes , type_info : TypeMetadata , types : Mapping [Ty , TypeMetadata ]) -> Value :
80
121
try :
81
122
return decode_value (data = data , type_info = type_info , types = types )
82
- except ValueError :
83
- return UnableToDecodeValue (data = data , type_info = type_info )
123
+ except ValueError as err :
124
+ return UnableToDecodeValue (f'Unable to decode value: { data !r } , of type: { type_info } : { err } ' )
84
125
85
126
86
127
def decode_value (data : bytes , type_info : TypeMetadata , types : Mapping [Ty , TypeMetadata ]) -> Value :
87
128
match type_info :
88
129
case Bool ():
89
130
return _decode_bool (data )
131
+ case Str ():
132
+ return _decode_str (data )
90
133
case Uint (int_ty ) | Int (int_ty ):
91
134
return _decode_int (data , int_ty )
92
135
case ArrayT (elem_ty , length ):
@@ -107,6 +150,10 @@ def _decode_bool(data: bytes) -> Value:
107
150
raise ValueError (f'Cannot decode as Bool: { data !r} ' )
108
151
109
152
153
+ def _decode_str (data : bytes ) -> Value :
154
+ return StrValue (data .decode ('utf-8' ))
155
+
156
+
110
157
def _decode_int (data : bytes , int_ty : IntTy | UintTy ) -> Value :
111
158
nbytes = int_ty .value
112
159
if len (data ) != nbytes :
0 commit comments