Memory.java

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2023 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package java.lang;

public final class Memory extends Object
{
    private static final int OFFSET_MONITOR = -0x04;
    private static final int OFFSET_OBJCLASS = +0x04;
    private static final int OFFSET_LENGTH = +0x08;
    private static final int OFFSET_DISPLACEMENT = +0x0c;
    private static final int OFFSET_CONTENT = +0x10;
    private static final int REF_TO_BLOCK = -0x04;
    private static final int BLOCK_TO_REF = -REF_TO_BLOCK;
    private static final int SIZE_HEAD_OBJECT = 0x0c;
    private static final int SIZE_HEAD_ARRAY = SIZE_HEAD_OBJECT + 0x08;

    private static boolean MULTI_THREADED;
    private static boolean RESERVE_ACTIVE;
    private static int STRING_POOL_OFFSET; /* СИСТЕМНОЕ ПОЛЕ */
    private static int DESCRIPTORS_SIZE; /* СИСТЕМНОЕ ПОЛЕ */
    private static int HEAP_LIMIT; /* СИСТЕМНОЕ ПОЛЕ */
    private static int HEAP_BEGIN;
    private static int HEAP_RESERVE;
    private static int DESCRIPTORS_COUNT;
    private static int DESCRIPTORS_RESERVE;
    private static long COLLECTED_BYTES;
    private static int[] FINALIZING_REF_STACK;
    private static long[] DESCRIPTORS;
    private static OutOfMemoryError LACK_MEMORY_ERROR;
    private static NoClassDefFoundError NOT_DEFINED_CLASS_ERROR;

    static {
        int len;
        int ref;
        int block = (len = STRING_POOL_OFFSET) + 0x04;
        for(int splen = MalikSystem.getIntAt(len), i = 0; i < splen; i++) block += needMemory(MalikSystem.getIntAt(block + (BLOCK_TO_REF + OFFSET_LENGTH)));
        ref = (block += -block & 0x0f) + BLOCK_TO_REF;
        MalikSystem.setIntAt(ref, 0);
        MalikSystem.setIntAt(ref + OFFSET_MONITOR, 0);
        MalikSystem.setIntAt(ref + OFFSET_OBJCLASS, 0);
        MalikSystem.setObjectAt(ref + OFFSET_OBJCLASS, MalikSystem.getClassInstance("[J"));
        MalikSystem.setIntAt(ref + OFFSET_LENGTH, len = (DESCRIPTORS_SIZE - SIZE_HEAD_ARRAY) >> 3);
        MalikSystem.setIntAt(ref + OFFSET_DISPLACEMENT, 0);
        block += (len << 3) + SIZE_HEAD_ARRAY;
        MULTI_THREADED = false;
        RESERVE_ACTIVE = false;
        HEAP_BEGIN = (block += (-block & 0x0f));
        HEAP_RESERVE = (HEAP_LIMIT - HEAP_BEGIN) / 20;
        DESCRIPTORS_COUNT = 0;
        DESCRIPTORS_RESERVE = len - len / 20;
        COLLECTED_BYTES = 0L;
        DESCRIPTORS = (long[]) MalikSystem.convertToObject(ref);
    }

    public static int getFree() {
        boolean thread;
        boolean status;
        int result;
        status = (thread = MULTI_THREADED) && MalikSystem.enterMonopolyAccess();
        try
        {
            result = free();
        }
        finally
        {
            if(thread) MalikSystem.leaveMonopolyAccess(status);
        }
        return result;
    }

    public static int getTotal() {
        return HEAP_LIMIT - HEAP_BEGIN;
    }

    public static long getCollected() {
        return COLLECTED_BYTES;
    }

    public static Object[] getAllObjects() {
        boolean thread;
        boolean status;
        Object[] result;
        status = (thread = MULTI_THREADED) && MalikSystem.enterMonopolyAccess();
        try
        {
            int rlen;
            result = new Object[rlen = DESCRIPTORS_COUNT - MalikSystem.arrayfindf_int(FINALIZING_REF_STACK, 0, 0)];
            for(int slen = FINALIZING_REF_STACK.length, dlen = DESCRIPTORS_COUNT, i, j = i = 0; i < rlen && j < dlen; j++)
            {
                int ref = (int) DESCRIPTORS[j] + BLOCK_TO_REF;
                Object reference;
                if(MalikSystem.arrayfindf_int(FINALIZING_REF_STACK, 0, ref) >= slen && (reference = MalikSystem.convertToObject(ref)) != result) result[i++] = reference;
            }
        }
        finally
        {
            if(thread) MalikSystem.leaveMonopolyAccess(status);
        }
        return result;
    }

    static void completeInit() {
        FINALIZING_REF_STACK = new int[0x20];
        LACK_MEMORY_ERROR = new OutOfMemoryError("Невозможно создать новый объект: динамическая память заполнена.");
        NOT_DEFINED_CLASS_ERROR = new NoClassDefFoundError("Невозможно создать новый объект: класс создаваемого объекта не указан.");
    }

    static void setMultiThreaded() {
        MULTI_THREADED = true;
    }

    static void checkSize(long refSize) throws OutOfMemoryError {
        if(refSize > 0x000000007fffffecL)
        {
            throw LACK_MEMORY_ERROR;
        }
    }

    static boolean isHeapAddress(int beginAddress, int endAddress) {
        return (beginAddress > STRING_POOL_OFFSET || endAddress > STRING_POOL_OFFSET) && (beginAddress < HEAP_LIMIT || endAddress < HEAP_LIMIT);
    }

    static int getStringPoolOffset() {
        return STRING_POOL_OFFSET;
    }

    static long collectGarbage() {
        long result = 0L;
        long lastRunTime = System.currentTimeMillis() - 1000L;
        label0: for(int i = 0; i < DESCRIPTORS_COUNT; i++)
        {
            boolean found;
            boolean status;
            int esp;
            int ebp;
            int ref;
            int block;
            long deallocated;
            long elapsedTime;
            long currentTime;

            /* Примерно каждую секунду удаляем все объекты с нулевым количеством ссылок на них */
            if((elapsedTime = (currentTime = System.currentTimeMillis()) - lastRunTime) >= 1000L || elapsedTime < 0L) for(lastRunTime = currentTime, block = ref = 0; ; )
            {
                status = MalikSystem.enterMonopolyAccess();
                try
                {
                    int index;
                    if(found = (index = DESCRIPTORS_COUNT - 1) >= 0 && (index = MalikSystem.findzerob(DESCRIPTORS, index, BLOCK_TO_REF)) >= 0)
                    {
                        FINALIZING_REF_STACK[0] = ref = (block = (int) DESCRIPTORS[index]) + BLOCK_TO_REF;
                    }
                }
                finally
                {
                    MalikSystem.leaveMonopolyAccess(status);
                }
                if(!found) break;
                try
                {
                    MalikSystem.convertToObject(ref).$finalize$();
                }
                catch(Throwable e)
                {
                }
                status = MalikSystem.enterMonopolyAccess();
                try
                {
                    deallocated = (long) deallocate(MalikSystem.blockfindb(DESCRIPTORS, DESCRIPTORS_COUNT - 1, block));
                    FINALIZING_REF_STACK[0] = 0;
                }
                finally
                {
                    MalikSystem.leaveMonopolyAccess(status);
                }
                result += deallocated;
                COLLECTED_BYTES += deallocated;
            }

            /* В остальное время: */
            /* 1. Удаляем объект, все ссылки на который ведут только из него самого */
            for(; ; )
            {
                if((ref = (block = (int) DESCRIPTORS[i]) + BLOCK_TO_REF) == BLOCK_TO_REF) break label0;
                if(StringPool.isMyOwnedObject(MalikSystem.convertToObject(ref))) continue label0;
                status = MalikSystem.enterMonopolyAccess();
                try
                {
                    if(found = MalikSystem.getIntAt(ref) == MalikSystem.convertToObject(ref).getQuantityOfReferencesTo(ref)) FINALIZING_REF_STACK[0] = ref;
                }
                finally
                {
                    MalikSystem.leaveMonopolyAccess(status);
                }
                if(!found) break;
                try
                {
                    MalikSystem.convertToObject(ref).$finalize$();
                }
                catch(Throwable e)
                {
                }
                status = MalikSystem.enterMonopolyAccess();
                try
                {
                    deallocated = (long) deallocate(MalikSystem.blockfindb(DESCRIPTORS, DESCRIPTORS_COUNT - 1, block));
                    FINALIZING_REF_STACK[0] = 0;
                }
                finally
                {
                    MalikSystem.leaveMonopolyAccess(status);
                }
                result += deallocated;
                COLLECTED_BYTES += deallocated;
            }

            /* 2. Удаляем «острова изоляции» (группа объектов, которые ссылаются друг на друга и не имеют других ссылок на себя) */
            if((ref = (int) DESCRIPTORS[i] + BLOCK_TO_REF) == BLOCK_TO_REF) break label0;
            if(StringPool.isMyOwnedObject(MalikSystem.convertToObject(ref))) continue label0;
            for(esp = 1, FINALIZING_REF_STACK[ebp = 0] = ref; ebp < esp; ebp++)
            {
                int refCount = 0;
                ref = FINALIZING_REF_STACK[ebp];
                for(int j = 0; j < DESCRIPTORS_COUNT; j++)
                {
                    int count;
                    int refAnot;
                    if((j & 0x03ff) == 0x03ff && ((elapsedTime = (currentTime = System.currentTimeMillis()) - lastRunTime) >= 1000L || elapsedTime < 0L))
                    {
                        for(lastRunTime = currentTime, block = refAnot = 0; ; )
                        {
                            status = MalikSystem.enterMonopolyAccess();
                            try
                            {
                                int index;
                                found =
                                    (index = DESCRIPTORS_COUNT - 1) >= 0 &&
                                    (index = MalikSystem.findzerob(DESCRIPTORS, index, BLOCK_TO_REF)) >= 0 &&
                                    push(refAnot = (block = (int) DESCRIPTORS[index]) + BLOCK_TO_REF, esp)
                                ;
                            }
                            finally
                            {
                                MalikSystem.leaveMonopolyAccess(status);
                            }
                            if(!found) break;
                            try
                            {
                                MalikSystem.convertToObject(refAnot).$finalize$();
                            }
                            catch(Throwable e)
                            {
                            }
                            status = MalikSystem.enterMonopolyAccess();
                            try
                            {
                                deallocated = (long) deallocate(MalikSystem.blockfindb(DESCRIPTORS, DESCRIPTORS_COUNT - 1, block));
                                FINALIZING_REF_STACK[esp] = 0;
                            }
                            finally
                            {
                                MalikSystem.leaveMonopolyAccess(status);
                            }
                            result += deallocated;
                            COLLECTED_BYTES += deallocated;
                        }
                    }
                    if((refAnot = (int) DESCRIPTORS[j] + BLOCK_TO_REF) == BLOCK_TO_REF) break;
                    status = MalikSystem.enterMonopolyAccess();
                    try
                    {
                        if(found = (count = MalikSystem.convertToObject(refAnot).getQuantityOfReferencesTo(ref)) > 0 && push(refAnot, esp)) esp++;
                    }
                    finally
                    {
                        MalikSystem.leaveMonopolyAccess(status);
                    }
                    if(found) refCount += count;
                }
                if(refCount < MalikSystem.getIntAt(ref))
                {
                    MalikSystem.arrayfill_int(FINALIZING_REF_STACK, 0, esp, 0);
                    continue label0;
                }
            }
            for(int j = esp; j-- > 0; )
            {
                try
                {
                    MalikSystem.convertToObject(FINALIZING_REF_STACK[j]).$finalize$();
                }
                catch(Throwable e)
                {
                }
            }
            for(int j = esp; j-- > 0; )
            {
                status = MalikSystem.enterMonopolyAccess();
                try
                {
                    deallocated = (long) deallocate(MalikSystem.blockfindb(DESCRIPTORS, DESCRIPTORS_COUNT - 1, FINALIZING_REF_STACK[j] + REF_TO_BLOCK));
                    if(j == 0) MalikSystem.arrayfill_int(FINALIZING_REF_STACK, 0, esp, 0);
                }
                finally
                {
                    MalikSystem.leaveMonopolyAccess(status);
                }
                result += deallocated;
                COLLECTED_BYTES += deallocated;
            }
        }
        return result;
    }

    static Object intern(Object instance) {
        boolean thread;
        boolean status;
        Object result;
        Class instanceClass;
        if((result = instance) == null) return null;
        instanceClass = instance.getClass();
        status = (thread = MULTI_THREADED) && MalikSystem.enterMonopolyAccess();
        try
        {
            for(int slen = FINALIZING_REF_STACK.length, i = 0; i < DESCRIPTORS_COUNT; i++)
            {
                int ref = (int) DESCRIPTORS[i] + BLOCK_TO_REF;
                if(
                    MalikSystem.arrayfindf_int(FINALIZING_REF_STACK, 0, ref) >= slen &&
                    MalikSystem.getObjectAt(ref + OFFSET_OBJCLASS) == instanceClass &&
                    MalikSystem.convertToObject(ref).equals(instance)
                )
                {
                    result = MalikSystem.convertToObject(ref);
                    break;
                }
            }
        }
        finally
        {
            if(thread) MalikSystem.leaveMonopolyAccess(status);
        }
        return result;
    }

    static Object internIgnore(Object instance) {
        boolean thread;
        boolean status;
        Object result;
        Class instanceClass;
        if((result = instance) == null) return null;
        instanceClass = instance.getClass();
        status = (thread = MULTI_THREADED) && MalikSystem.enterMonopolyAccess();
        try
        {
            for(int slen = FINALIZING_REF_STACK.length, i = 0; i < DESCRIPTORS_COUNT; i++)
            {
                int ref = (int) DESCRIPTORS[i] + BLOCK_TO_REF;
                if(
                    MalikSystem.arrayfindf_int(FINALIZING_REF_STACK, 0, ref) >= slen && ref != MalikSystem.convertToReference(instance) &&
                    MalikSystem.getObjectAt(ref + OFFSET_OBJCLASS) == instanceClass && MalikSystem.convertToObject(ref).equals(instance)
                )
                {
                    result = MalikSystem.convertToObject(ref);
                    break;
                }
            }
        }
        finally
        {
            if(thread) MalikSystem.leaveMonopolyAccess(status);
        }
        return result;
    }

    static Object allocateInstanceOf(Class instanceClass, int refSize, int arrayLength) throws OutOfMemoryError, NoClassDefFoundError {
        boolean thread;
        boolean status;
        int blockSize;
        Object result;
        if(instanceClass == null)
        {
            throw NOT_DEFINED_CLASS_ERROR;
        }
        if(refSize <= 0) return null;
        blockSize = refSize - REF_TO_BLOCK;
        refSize = (blockSize += -blockSize & 0x0f) - BLOCK_TO_REF;
        status = (thread = MULTI_THREADED) && MalikSystem.enterMonopolyAccess();
        try
        {
            result = makeObject(instanceClass, allocate(blockSize) + BLOCK_TO_REF, refSize, arrayLength);
        }
        finally
        {
            if(thread) MalikSystem.leaveMonopolyAccess(status);
        }
        return result;
    }

    private static void outOfMemory() throws OutOfMemoryError {
        if(RESERVE_ACTIVE)
        {
            /* Выходим из программы, если резерв памяти был исчерпан */
            MalikSystem.syscall((long) Thread.MAIN_THREAD_ID, 0x0001);
        }
        RESERVE_ACTIVE = true;
        throw LACK_MEMORY_ERROR;
    }

    private static boolean push(int ref, int top) {
        if(MalikSystem.arrayfindf_int(FINALIZING_REF_STACK, 0, ref) < top) return false;
        if(top == FINALIZING_REF_STACK.length) MalikSystem.arraycopyf_int(FINALIZING_REF_STACK, 0, FINALIZING_REF_STACK = new int[top << 1], 0, top);
        FINALIZING_REF_STACK[top] = ref;
        return true;
    }

    private static int free() {
        int i;
        long descriptor;
        return HEAP_LIMIT - ((i = DESCRIPTORS_COUNT - 1) >= 0 ? (int) (descriptor = DESCRIPTORS[i]) + (int) (descriptor >> 32) : HEAP_BEGIN);
    }

    private static int allocate(int blockSize) throws OutOfMemoryError {
        int i;
        int result;
        long descriptor;
        if(RESERVE_ACTIVE && (DESCRIPTORS_COUNT < DESCRIPTORS_RESERVE || free() > HEAP_RESERVE)) RESERVE_ACTIVE = false;
        if(DESCRIPTORS_COUNT >= (RESERVE_ACTIVE ? DESCRIPTORS.length : DESCRIPTORS_RESERVE)) outOfMemory();
        if(DESCRIPTORS_COUNT == 0)
        {
            if(blockSize > HEAP_LIMIT - HEAP_BEGIN) outOfMemory();
            DESCRIPTORS[DESCRIPTORS_COUNT++] = makeDescriptor(HEAP_BEGIN, blockSize);
            return HEAP_BEGIN;
        }
        if((i = MalikSystem.findfreef(DESCRIPTORS, 0, blockSize)) > 0 && i < DESCRIPTORS_COUNT)
        {
            MalikSystem.arraycopyb_long(DESCRIPTORS, DESCRIPTORS_COUNT, DESCRIPTORS, DESCRIPTORS_COUNT + 1, DESCRIPTORS_COUNT++ - i);
            DESCRIPTORS[i] = makeDescriptor(result = (int) DESCRIPTORS[i] - blockSize, blockSize);
            return result;
        }
        i = DESCRIPTORS_COUNT - 1;
        if(blockSize > HEAP_LIMIT - (result = (int) (descriptor = DESCRIPTORS[i]) + (int) (descriptor >> 32))) outOfMemory();
        DESCRIPTORS[DESCRIPTORS_COUNT++] = makeDescriptor(result, blockSize);
        return result;
    }

    private static int deallocate(int blockIndex) {
        if(blockIndex >= 0)
        {
            int result = (int) (DESCRIPTORS[blockIndex] >> 32);
            MalikSystem.arraycopyf_long(DESCRIPTORS, blockIndex + 1, DESCRIPTORS, blockIndex, --DESCRIPTORS_COUNT - blockIndex);
            DESCRIPTORS[DESCRIPTORS_COUNT] = 0L;
            return result;
        }
        return 0;
    }

    private static int needMemory(int length) {
        return length + (-length & 3) + SIZE_HEAD_ARRAY;
    }

    private static long makeDescriptor(int block, int blockSize) {
        return (long) blockSize << 32 | (long) block & 0x00000000ffffffffL;
    }

    private static Object makeObject(Class instanceClass, int ref, int refSize, int arrayLength) {
        Object result;
        MalikSystem.setIntAt(ref, 0);
        MalikSystem.setIntAt(ref + OFFSET_MONITOR, 0);
        MalikSystem.setIntAt(ref + OFFSET_OBJCLASS, 0);
        MalikSystem.setObjectAt(ref + OFFSET_OBJCLASS, instanceClass);
        result = MalikSystem.convertToObject(ref);
        if(refSize > OFFSET_CONTENT)
        {
            int len;
            MalikSystem.setIntAt(ref + OFFSET_LENGTH, len = (refSize - 0x10) >> 2);
            MalikSystem.setIntAt(ref + OFFSET_DISPLACEMENT, 0);
            MalikSystem.arrayfill_int(result, 0, len, 0);
        }
        MalikSystem.setIntAt(ref + OFFSET_LENGTH, arrayLength >= 0 ? arrayLength : 0);
        return result;
    }

    private Memory() {
    }
}

final class StringPool extends Object
{
    private static final int LENGTH;
    private static final byte[][] CODES;
    private static final String[] STRINGS;

    static {
        int address;
        int length = MalikSystem.getIntAt(address = Memory.getStringPoolOffset());
        byte[][] codes;
        address += 0x04;
        LENGTH = length;
        CODES = codes = new byte[length][];
        STRINGS = new String[length];
        for(int i = 0; i < length; i++)
        {
            int size = (codes[i] = readArray(address)).length;
            address += size + (-size & 0x03) + 0x14;
        }
        Memory.completeInit();
        Thread.completeInit();
    }

    public static int getLength() {
        return LENGTH;
    }

    public static String getString(int index) {
        boolean flag;
        String[] strings;
        String result;
        if(index >= LENGTH || index < 0) return null;
        if((result = (strings = STRINGS)[index]) != null) return result;
        flag = MalikSystem.enterMonopolyAccess();
        try
        {
            if(strings[index] == null) strings[index] = result = (String) Memory.internIgnore(new String(CODES[index]));
        }
        finally
        {
            MalikSystem.leaveMonopolyAccess(flag);
        }
        return result;
    }

    static boolean isMyOwnedObject(Object obj) {
        return obj == CODES || obj == STRINGS || Array.findf(STRINGS, 0, obj) < LENGTH;
    }

    static String intern(String string) {
        String[] strings = STRINGS;
        String result = string;
        for(int index = LENGTH; index-- > 0; )
        {
            String current;
            if((current = strings[index]) != null && current.equals(string))
            {
                result = current;
                break;
            }
        }
        return result;
    }

    private static byte[] readArray(int address) {
        return (byte[]) MalikSystem.convertToObject(address + 0x04);
    }

    private StringPool() {
    }
}