1
+ import builtins
1
2
import itertools
2
3
import logging
3
4
import math
17
18
if TYPE_CHECKING :
18
19
from claripy .annotation import Annotation
19
20
20
- try :
21
- import _pickle as pickle
22
- except ImportError :
23
- import pickle
24
-
25
- try :
26
- # Python's build-in MD5 is about 2x faster than hashlib.md5 on short bytestrings
27
- import _md5 as md5
28
- except ImportError :
29
- import hashlib as md5
30
-
31
21
l = logging .getLogger ("claripy.ast" )
32
22
33
23
WORKER = bool (os .environ .get ("WORKER" , False ))
@@ -69,7 +59,7 @@ def _make_name(name: str, size: int, explicit_name: bool = False, prefix: str =
69
59
return name
70
60
71
61
72
- def _d (h , cls , state ):
62
+ def _unpickle (h , cls , state ):
73
63
"""
74
64
This function is the deserializer for ASTs.
75
65
It exists to work around the fact that pickle will (normally) call __new__() with no arguments during
@@ -132,9 +122,6 @@ def __new__(cls, op, args, add_variables=None, hash=None, **kwargs): # pylint:d
132
122
:param annotations: A frozenset of annotations applied onto this AST.
133
123
"""
134
124
135
- # if any(isinstance(a, BackendObject) for a in args):
136
- # raise Exception('asdf')
137
-
138
125
a_args = args if type (args ) is tuple else tuple (args )
139
126
140
127
# initialize the following properties: symbolic, variables and errored
@@ -252,17 +239,17 @@ def __new__(cls, op, args, add_variables=None, hash=None, **kwargs): # pylint:d
252
239
elif op in {"BVS" , "BVV" , "BoolS" , "BoolV" , "FPS" , "FPV" } and not annotations :
253
240
if op == "FPV" and a_args [0 ] == 0.0 and math .copysign (1 , a_args [0 ]) < 0 :
254
241
# Python does not distinguish between +0.0 and -0.0 so we add sign to tuple to distinguish
255
- h = ( op , kwargs .get ("length" , None ), ("-" , * a_args ))
242
+ h = builtins . hash (( op , kwargs .get ("length" , None ), ("-" , * a_args ) ))
256
243
elif op == "FPV" and math .isnan (a_args [0 ]):
257
244
# cannot compare nans
258
- h = ( op , kwargs .get ("length" , None ), ("nan" ,) + a_args [1 :])
245
+ h = builtins . hash (( op , kwargs .get ("length" , None ), ("nan" ,) + a_args [1 :]) )
259
246
else :
260
- h = ( op , kwargs .get ("length" , None ), a_args )
247
+ h = builtins . hash (( op , kwargs .get ("length" , None ), a_args ) )
261
248
262
249
cache = cls ._leaf_cache
263
250
else :
264
251
h = Base ._calc_hash (op , a_args , kwargs ) if hash is None else hash
265
- self = cache .get (h , None )
252
+ self = cache .get (h & 0x7FFF_FFFF_FFFF_FFFF , None )
266
253
if self is None :
267
254
self = super ().__new__ (
268
255
cls ,
@@ -282,8 +269,8 @@ def __new__(cls, op, args, add_variables=None, hash=None, **kwargs): # pylint:d
282
269
relocatable_annotations = relocatable_annotations ,
283
270
** kwargs ,
284
271
)
285
- self ._hash = h
286
- cache [h ] = self
272
+ self ._hash = h & 0x7FFF_FFFF_FFFF_FFFF
273
+ cache [self . _hash ] = self
287
274
# else:
288
275
# if self.args != a_args or self.op != op or self.variables != kwargs['variables']:
289
276
# raise Exception("CRAP -- hash collision")
@@ -296,7 +283,7 @@ def __init_with_annotations__(
296
283
):
297
284
cache = cls ._hash_cache
298
285
h = Base ._calc_hash (op , a_args , kwargs )
299
- self = cache .get (h , None )
286
+ self = cache .get (h & 0x7FFF_FFFF_FFFF_FFFF , None )
300
287
if self is not None :
301
288
return self
302
289
@@ -318,15 +305,15 @@ def __init_with_annotations__(
318
305
** kwargs ,
319
306
)
320
307
321
- self ._hash = h
322
- cache [h ] = self
308
+ self ._hash = h & 0x7FFF_FFFF_FFFF_FFFF
309
+ cache [self . _hash ] = self
323
310
324
311
return self
325
312
326
313
def __reduce__ (self ):
327
314
# HASHCONS: these attributes key the cache
328
315
# BEFORE CHANGING THIS, SEE ALL OTHER INSTANCES OF "HASHCONS" IN THIS FILE
329
- return _d , (
316
+ return _unpickle , (
330
317
self ._hash ,
331
318
self .__class__ ,
332
319
(self .op , self .args , self .length , self .variables , self .symbolic , self .annotations ),
@@ -335,113 +322,6 @@ def __reduce__(self):
335
322
def __init__ (self , * args , ** kwargs ):
336
323
pass
337
324
338
- @staticmethod
339
- def _calc_hash (op , args , keywords ):
340
- """
341
- Calculates the hash of an AST, given the operation, args, and kwargs.
342
-
343
- :param op: The operation.
344
- :param args: The arguments to the operation.
345
- :param keywords: A dict including the 'symbolic', 'variables', and 'length' items.
346
- :returns: a hash.
347
-
348
- We do it using md5 to avoid hash collisions.
349
- (hash(-1) == hash(-2), for example)
350
- """
351
- args_tup = tuple (a if type (a ) in (int , float ) else getattr (a , "_hash" , hash (a )) for a in args )
352
- # HASHCONS: these attributes key the cache
353
- # BEFORE CHANGING THIS, SEE ALL OTHER INSTANCES OF "HASHCONS" IN THIS FILE
354
-
355
- to_hash = Base ._ast_serialize (op , args_tup , keywords )
356
- if to_hash is None :
357
- # fall back to pickle.dumps
358
- to_hash = (
359
- op ,
360
- args_tup ,
361
- str (keywords .get ("length" , None )),
362
- hash (keywords ["variables" ]),
363
- keywords ["symbolic" ],
364
- hash (keywords .get ("annotations" , None )),
365
- )
366
- to_hash = pickle .dumps (to_hash , - 1 )
367
-
368
- # Why do we use md5 when it's broken? Because speed is more important
369
- # than cryptographic integrity here. Then again, look at all those
370
- # allocations we're doing here... fast python is painful.
371
- hd = md5 .md5 (to_hash ).digest ()
372
- return md5_unpacker .unpack (hd )[0 ] # 64 bits
373
-
374
- @staticmethod
375
- def _arg_serialize (arg ) -> bytes | None :
376
- if arg is None :
377
- return b"\x0f "
378
- elif arg is True :
379
- return b"\x1f "
380
- elif arg is False :
381
- return b"\x2e "
382
- elif isinstance (arg , int ):
383
- if arg < 0 :
384
- if arg >= - 0x7FFF :
385
- return b"-" + struct .pack ("<h" , arg )
386
- elif arg >= - 0x7FFF_FFFF :
387
- return b"-" + struct .pack ("<i" , arg )
388
- elif arg >= - 0x7FFF_FFFF_FFFF_FFFF :
389
- return b"-" + struct .pack ("<q" , arg )
390
- return None
391
- else :
392
- if arg <= 0xFFFF :
393
- return struct .pack ("<H" , arg )
394
- elif arg <= 0xFFFF_FFFF :
395
- return struct .pack ("<I" , arg )
396
- elif arg <= 0xFFFF_FFFF_FFFF_FFFF :
397
- return struct .pack ("<Q" , arg )
398
- return None
399
- elif isinstance (arg , str ):
400
- return arg .encode ()
401
- elif isinstance (arg , float ):
402
- return struct .pack ("f" , arg )
403
- elif isinstance (arg , tuple ):
404
- arr = []
405
- for elem in arg :
406
- b = Base ._arg_serialize (elem )
407
- if b is None :
408
- return None
409
- arr .append (b )
410
- return b"" .join (arr )
411
-
412
- return None
413
-
414
- @staticmethod
415
- def _ast_serialize (op : str , args_tup , keywords ) -> bytes | None :
416
- """
417
- Serialize the AST and get a bytestring for hashing.
418
-
419
- :param op: The operator.
420
- :param args_tup: A tuple of arguments.
421
- :param keywords: A dict of keywords.
422
- :return: The serialized bytestring.
423
- """
424
-
425
- serialized_args = Base ._arg_serialize (args_tup )
426
- if serialized_args is None :
427
- return None
428
-
429
- if "length" in keywords :
430
- length = Base ._arg_serialize (keywords ["length" ])
431
- if length is None :
432
- return None
433
- else :
434
- length = b"none"
435
-
436
- variables = struct .pack ("<Q" , hash (keywords ["variables" ]) & 0xFFFF_FFFF_FFFF_FFFF )
437
- symbolic = b"\x01 " if keywords ["symbolic" ] else b"\x00 "
438
- if "annotations" in keywords :
439
- annotations = struct .pack ("<Q" , hash (keywords ["annotations" ]) & 0xFFFF_FFFF_FFFF_FFFF )
440
- else :
441
- annotations = b"\xf9 "
442
-
443
- return op .encode () + serialized_args + length + variables + symbolic + annotations
444
-
445
325
# pylint:disable=attribute-defined-outside-init
446
326
def __a_init__ (
447
327
self ,
@@ -523,12 +403,6 @@ def _encoded_name(self):
523
403
# Collapsing and simplification
524
404
#
525
405
526
- # def _models_for(self, backend):
527
- # for a in self.args:
528
- # backend.convert_expr(a)
529
- # else:
530
- # yield backend.convert(a)
531
-
532
406
def make_like (self : T , op : str , args : Iterable , ** kwargs ) -> T :
533
407
# Try to simplify the expression again
534
408
simplified = simplifications .simpleton .simplify (op , args ) if kwargs .pop ("simplify" , False ) is True else None
0 commit comments