All Downloads are FREE. Search and download functionalities are using the official Maven repository.

Lib.Krakatau.constant_pool.py Maven / Gradle / Ivy

import struct, collections

#ConstantPool stores strings as strings or unicodes. They are automatically
#converted to and from modified Utf16 when reading and writing to binary

#Floats and Doubles are internally stored as integers with the same bit pattern
#Since using raw floats breaks equality testing for signed zeroes and NaNs
#cpool.getArgs/getArgsCheck will automatically convert them into Python floats

def decodeStr((s,)):
    return s.replace('\xc0\x80','\0').decode('utf8'),
def encodeStr((u,)):
    return u.encode('utf8').replace('\0','\xc0\x80'),
def strToBytes(args):
    s = encodeStr(args)[0]
    return struct.pack('>H',len(s)) + s

def decodeFloat(i):
    return struct.unpack('>f', struct.pack('>i', i)) #Note: returns tuple
def decodeDouble(i):
    return struct.unpack('>d', struct.pack('>q', i))

cpoolInfo_t = collections.namedtuple('cpoolInfo_t',
                                     ['name','tag','recoverArgs','toBytes'])

Utf8 = cpoolInfo_t('Utf8',1,
                  (lambda self,(s,):(s,)),
                  strToBytes)

Class = cpoolInfo_t('Class',7,
                    (lambda self,(n_id,):self.getArgs(n_id)),
                    (lambda (n_id,): struct.pack('>H',n_id)))

NameAndType = cpoolInfo_t('NameAndType',12,
                (lambda self,(n,d):self.getArgs(n) + self.getArgs(d)),
                (lambda (n,d): struct.pack('>HH',n,d)))

Field = cpoolInfo_t('Field',9,
                (lambda self,(c_id, nat_id):self.getArgs(c_id) + self.getArgs(nat_id)),
                (lambda (n,d): struct.pack('>HH',n,d)))

Method = cpoolInfo_t('Method',10,
                (lambda self,(c_id, nat_id):self.getArgs(c_id) + self.getArgs(nat_id)),
                (lambda (n,d): struct.pack('>HH',n,d)))

InterfaceMethod = cpoolInfo_t('InterfaceMethod',11,
                (lambda self,(c_id, nat_id):self.getArgs(c_id) + self.getArgs(nat_id)),
                (lambda (n,d): struct.pack('>HH',n,d)))

String = cpoolInfo_t('String',8,
                (lambda self,(n_id,):self.getArgs(n_id)),
                (lambda (n_id,): struct.pack('>H',n_id)))

Int = cpoolInfo_t('Int',3,
                  (lambda self,(s,):(s,)),
                  (lambda (val,): struct.pack('>i',val)))

Long = cpoolInfo_t('Long',5,
                  (lambda self,(s,):(s,)),
                  (lambda (val,): struct.pack('>q',val)))

Float = cpoolInfo_t('Float',4,
                  (lambda self,(s,):decodeFloat(s)),
                  (lambda (val,): struct.pack('>i',val)))

Double = cpoolInfo_t('Double',6,
                  (lambda self,(s,):decodeDouble(s)),
                  (lambda (val,): struct.pack('>q',val)))

MethodHandle = cpoolInfo_t('MethodHandle',15,
                (lambda self,(t, n_id):(t,)+self.getArgs(n_id)),
                (lambda (t, n_id): struct.pack('>BH',t, n_id)))

MethodType = cpoolInfo_t('MethodType',16,
                (lambda self,(n_id,):self.getArgs(n_id)),
                (lambda (n_id,): struct.pack('>H',n_id)))

InvokeDynamic = cpoolInfo_t('InvokeDynamic',18,
                (lambda self,(bs_id, nat_id):(bs_id,) + self.getArgs(nat_id)),
                (lambda (n,d): struct.pack('>HH',n,d)))

cpoolTypes = [Utf8, Class, NameAndType, Field, Method, InterfaceMethod,
              String, Int, Long, Float, Double, 
              MethodHandle, MethodType, InvokeDynamic]
name2Type = {t.name:t for t in cpoolTypes}
tag2Type = {t.tag:t for t in cpoolTypes}

class ConstPool(object):
    def __init__(self, initialData=((None,None),)):
        self.pool = []
        self.reserved = set()
        self.available = set()

        for tag, val in initialData:
            if tag is None:
                self.addEmptySlot()
            else:
                t = tag2Type[tag]
                if t.name == 'Utf8':
                    val = decodeStr(val)
                self.pool.append((t.name, val))

    def size(self): #Number of slots including gaps, not number of entries
        return len(self.pool)
    def getPoolIter(self):
        return (x for x in self.pool if x[0] is not None)
    def getEnumeratePoolIter(self):
        return ((i,x) for i,x in enumerate(self.pool) if x[0] is not None)

    def addEmptySlot(self):
        self.pool.append((None, None))

    def getAvailableIndex(self):
        if self.available:
            return self.available.pop()
        while len(self.pool) in self.reserved:
            self.addEmptySlot()
        self.addEmptySlot()
        return len(self.pool)-1    

    def getAvailableIndex2(self):
        for i in self.available:
            if i+1 in self.available:
                self.available.remove(i)
                self.available.remove(i+1)
                return i

        while len(self.pool) in self.reserved or len(self.pool)+1 in self.reserved:
            self.addEmptySlot()
        self.addEmptySlot()
        self.addEmptySlot()
        return len(self.pool)-2

    # Special function for assembler
    def addItem(self, item, index=None):
        if index is None and item in self.pool:
            return self.pool.index(item)

        if item[0] == 'Utf8':
            assert(isinstance(item[1][0], basestring))
        cat2 = item[0] in ('Long','Double')

        if index is None:
            index = self.getAvailableIndex2() if cat2 else self.getAvailableIndex()
        else:
            temp = len(self.pool)
            if index >= temp:
                #If desired slot is past the end of current range, add a bunch of placeholder slots
                self.pool += [(None,None)] * (index+1-temp)
                self.available.update(range(temp,index))
                self.available -= self.reserved

            self.reserved.remove(index)
            if cat2:
                self.reserved.remove(index+1)
                self.addEmptySlot()

        assert(index not in self.reserved)
        self.pool[index] = item
        return index

    def copyItem(self, src, index):
        return self.addItem(self.pool[src], index=index)

    # Accessors ######################################################################
    def getArgs(self, i):
        if not (i >= 0 and i 1 else val[0]

    def getType(self, index): return self.pool[index][0]

    ##################################################################################
    def fillPlaceholders(self):
        #fill in all the placeholder slots with a dummy reference. Class and String items
        #have the smallest size (3 bytes). There should always be an existing class item
        #we can copy
        dummy = next(item for item in self.pool if item[0] == 'Class')
        for i in self.available:
            self.pool[i] = dummy

    def bytes(self):
        parts = []
        pool = self.pool

        assert(not self.reserved)
        self.fillPlaceholders()

        assert(len(pool) <= 65535)
        parts.append(struct.pack('>H',len(pool)))
        
        for name, vals in self.getPoolIter():
            t = name2Type[name]
            parts.append(struct.pack('>B',t.tag))
            parts.append(t.toBytes(vals))
        return ''.join(parts)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy