BinarySourceGenerator.avt

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

/*
    Генератор кода для языка программирования
    Объектно-ориентированный продвинутый векторный транслятор

    Copyright © 2021, 2024 Малик Разработчик

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

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

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

package ru.malik.elaborarer.avtoo.generator;

import avt.io.*;
import avt.io.charset.*;
import avt.util.*;
import platform.independent.filesystem.*;
import ru.malik.elaborarer.avtoo.compiler.StringArray;
import ru.malik.elaborarer.avtoo.lang.*;
import ru.malik.elaborarer.compression.zlib.*;

public class BinarySourceGenerator(CodeGenerator, AVTOOConstants, AVTOORecompilable, AVTOOService)
{
    /*<fold уровни создания файлов двоичного исходного кода>*/
    public static final int CLASS   = 0;
    public static final int PACKAGE = 1;
    public static final int LIBRARY = 2;
    /*</fold>*/

    private static void appendClassType(ClassType type, BinarySourceGeneratorConstants constants, Vector container) throws IOException {
        for(container + constants.serializeClassType(type), boolean isStruct = type.isStruct(), int length = type.length, int index = 0; index < length; index++)
        {
            Code code = null;
            label0:
            {
                Member member = type[index];
                if(member instanceof Method)
                {
                    Method method = (Method) member;
                    code = method.code;
                    container + constants.serializeMethod(method);
                    break label0;
                }
                if(member instanceof Callable)
                {
                    Callable callable = (Callable) member;
                    code = callable.code;
                    container + constants.serializeCallable(callable);
                    break label0;
                }
                if(member instanceof Property)
                {
                    container + constants.serializeProperty((Property) member);
                    continue;
                }
                if(member instanceof Field)
                {
                    container + (isStruct ? constants.serializeStructField((Field) member) : constants.serializeClassField((Field) member));
                }
                continue;
            }
            if(code != null) container + constants.serializeCode(code);
        }
    }

    private static void createRecompilable(Vector container, FileSystem fileSystem, String recompilablePath) throws IOException {
        byte[][] chunks = new byte[container.length][];
        container.copyInto(chunks, 0);
        ByteWriter stream = fileSystem.createFile(recompilablePath + BINARY_SOURCE_EXTENSION);
        try
        {
            with((new DataOutputStream(stream)).bigEndianDataOutput, (Checksum32) new CRC32())
            {
                writeLong(AVTOORecompilable.SIGNATURE_AVTR);
                for(int clength = chunks.length, int cindex = 0; cindex < clength; cindex++)
                {
                    byte[] dbytes = chunks[cindex];
                    int dlength = dbytes.length;
                    update(dbytes, 0, dlength);
                    writeInt(dlength - 4 >> 1);
                    write(dbytes);
                    writeInt(value);
                    reset();
                }
            }
            stream.flush();
        } finally
        {
            stream.close();
        }
    }

    private static Hashtable createSourcesTable(SourceArray sources) {
        Hashtable result = new Hashtable();
        for(int index = sources.length; index-- > 0; )
        {
            TextSource source = (TextSource) sources[index];
            Package owner = source.owner;
            if(owner == null) continue;
            String name = owner.specialCanonicalName;
            (Vector) (result.containsKey(name) ? result[name] : result[name] = new Vector()) + source;
        }
        return result;
    }

    private int fldLevel;
    private String fldOverwritten;

    public (int pointerSize): super(pointerSize, true) { fldLevel = CLASS; }

    public void compile() throws IOException, CompilerException {
        /* -- предыдущие стадии -- */
        super.compile();
        /* 8. Стадия выгрузки данных */
        Charset utf16 = Charset.get("UTF-16 BE");
        Library project = project;
        FileSystem fileSystem = project.fileSystem;
        String destinationPath = fldOverwritten = prepareDestinationPath(fileSystem, project.directoryPath + BINARY_SOURCE_PATH);
        Vector container = new Vector();
        BinarySourceGeneratorConstants constants = new BinarySourceGeneratorConstants();
        byte[] version = BinarySourceGeneratorConstants.serializeVersion();
        byte[] end = BinarySourceGeneratorConstants.serializeEnd();
        switch(fldLevel)
        {
        case LIBRARY:
            {
                container + version;
                for(Hashtable sourcesTable = createSourcesTable(project.sources), int plength = project.length, int pindex = 0; pindex < plength; pindex++)
                {
                    Package pack = project[pindex];
                    String packageName = pack.specialCanonicalName;
                    if(packageName.isEmpty()) continue;
                    Vector sourcesList = (Vector) sourcesTable[packageName];
                    TextSource[] sourcesArray = null;
                    if(sourcesList != null) sourcesList.copyInto(sourcesArray = new TextSource[sourcesList.length], 0);
                    container + constants.serializePackage(pack);
                    if(sourcesArray != null) for(int slength = sourcesArray.length, int sindex = 0; sindex < slength; sindex++)
                    {
                        TextSource source = sourcesArray[sindex];
                        container + BinarySourceGeneratorConstants.serializeTextSource(source, utf16);
                        for(ClassTypeArray declared = source.declared, int dlength = declared.length, int dindex = 0; dindex < dlength; dindex++)
                        {
                            appendClassType(declared[dindex], constants, container);
                        }
                    }
                }
                container.insert(1, constants.serializeConstants(utf16));
                createRecompilable(container + end, fileSystem, destinationPath + "!library");
                container.clear();
                constants.clear();
            }
            break;
        case PACKAGE:
            for(Hashtable sourcesTable = createSourcesTable(project.sources), int plength = project.length, int pindex = 0; pindex < plength; pindex++)
            {
                Package pack = project[pindex];
                String packageName = pack.specialCanonicalName;
                if(packageName.isEmpty()) continue;
                Vector sourcesList = (Vector) sourcesTable[packageName];
                TextSource[] sourcesArray = null;
                if(sourcesList != null) sourcesList.copyInto(sourcesArray = new TextSource[sourcesList.length], 0);
                container + version + constants.serializePackage(pack);
                if(sourcesArray != null) for(int slength = sourcesArray.length, int sindex = 0; sindex < slength; sindex++)
                {
                    TextSource source = sourcesArray[sindex];
                    container + BinarySourceGeneratorConstants.serializeTextSource(source, utf16);
                    for(ClassTypeArray declared = source.declared, int dlength = declared.length, int dindex = 0; dindex < dlength; dindex++)
                    {
                        appendClassType(declared[dindex], constants, container);
                    }
                }
                container.insert(1, constants.serializeConstants(utf16));
                createRecompilable(container + end, fileSystem, destinationPath + packageName);
                container.clear();
                constants.clear();
            }
            break;
        default:
            for(int plength = project.length, int pindex = 0; pindex < plength; pindex++)
            {
                Package pack = project[pindex];
                String packageName = pack.specialCanonicalName;
                if(packageName.isEmpty()) continue;
                String directoryPath = destinationPath + packageName;
                fileSystem.createDirectory(directoryPath);
                directoryPath = directoryPath + '/';
                {
                    container + version + constants.serializePackage(pack);
                    container.insert(1, constants.serializeConstants(utf16));
                    createRecompilable(container + end, fileSystem, directoryPath + "!package");
                    container.clear();
                    constants.clear();
                }
                for(int dlength = pack.length, int dindex = 0; dindex < dlength; dindex++)
                {
                    ClassType type = (ClassType) pack[dindex];
                    if(type instanceof ArrayType) continue;
                    appendClassType(type, constants, container + version + BinarySourceGeneratorConstants.serializeTextSource((TextSource) type.source, utf16));
                    container.insert(1, constants.serializeConstants(utf16));
                    createRecompilable(container + end, fileSystem, directoryPath + type.specialSimpleName);
                    container.clear();
                    constants.clear();
                }
            }
        }
        /* -- конец процесса компиляции -- */
    }

    public final String overwritten { read = fldOverwritten }

    public final int level { read = fldLevel, write = fldLevel = value < CLASS ? CLASS : value > LIBRARY ? LIBRARY : value }
}

service BinarySourceGeneratorConstants(Vector, AVTOOConstants, AVTOORecompilable)
{
    public static byte[] serializeVersion() throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_VERS);
            writeInt(VERSION_AVTR);
        }
        return stream.toByteArray();
    }

    public static byte[] serializeTextSource(TextSource source, Charset charset) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_SURC);
            write(source.outputPath.toByteArray(charset));
        }
        return stream.toByteArray();
    }

    public static byte[] serializeEnd() throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_IEND);
        }
        return stream.toByteArray();
    }

    public void clear() {
        super.clear();
        operator +(null);
    }

    public byte[] serializeConstants(Charset charset) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_CONS);
            for(Object[] elements = fldElements, int elength = fldLength, int eindex = 1; eindex < elength; eindex++)
            {
                Object element = elements[eindex];
                if(element == null)
                {
                    throw new UnencodableDataException(package.getResourceString("not-encodable.null-pointer"));
                }
                Class type = element.getClass();
                if(Boolean.class == type)
                {
                    writeShort(CONST_Z);
                    writeShort(((Boolean) element).booleanValue ? -1 : 0);
                    continue;
                }
                if(Char.class == type)
                {
                    writeShort(CONST_C);
                    writeShort(((Char) element).charValue);
                    continue;
                }
                if(Real.class == type)
                {
                    writeShort(CONST_R);
                    writeReal(((Real) element).realValue);
                    continue;
                }
                if(Double.class == type)
                {
                    writeShort(CONST_D);
                    writeDouble(((Double) element).doubleValue);
                    continue;
                }
                if(Double2.class == type)
                {
                    writeShort(CONST_D2);
                    writeDouble2(((Double2) element).double2Value);
                    continue;
                }
                if(Double4.class == type)
                {
                    writeShort(CONST_D4);
                    writeDouble4(((Double4) element).double4Value);
                    continue;
                }
                if(Double8.class == type)
                {
                    writeShort(CONST_D8);
                    writeDouble8(((Double8) element).double8Value);
                    continue;
                }
                if(Float.class == type)
                {
                    writeShort(CONST_F);
                    writeFloat(((Float) element).floatValue);
                    continue;
                }
                if(Float2.class == type)
                {
                    writeShort(CONST_F2);
                    writeFloat2(((Float2) element).float2Value);
                    continue;
                }
                if(Float4.class == type)
                {
                    writeShort(CONST_F4);
                    writeFloat4(((Float4) element).float4Value);
                    continue;
                }
                if(Float8.class == type)
                {
                    writeShort(CONST_F8);
                    writeFloat8(((Float8) element).float8Value);
                    continue;
                }
                if(Byte.class == type)
                {
                    writeShort(CONST_B);
                    writeShort(((Byte) element).byteValue);
                    continue;
                }
                if(Byte2.class == type)
                {
                    writeShort(CONST_B2);
                    writeByte2(((Byte2) element).byte2Value);
                    continue;
                }
                if(Byte4.class == type)
                {
                    writeShort(CONST_B4);
                    writeByte4(((Byte4) element).byte4Value);
                    continue;
                }
                if(Byte8.class == type)
                {
                    writeShort(CONST_B8);
                    writeByte8(((Byte8) element).byte8Value);
                    continue;
                }
                if(Short.class == type)
                {
                    writeShort(CONST_S);
                    writeShort(((Short) element).shortValue);
                    continue;
                }
                if(Short2.class == type)
                {
                    writeShort(CONST_S2);
                    writeShort2(((Short2) element).short2Value);
                    continue;
                }
                if(Short4.class == type)
                {
                    writeShort(CONST_S4);
                    writeShort4(((Short4) element).short4Value);
                    continue;
                }
                if(Short8.class == type)
                {
                    writeShort(CONST_S8);
                    writeShort8(((Short8) element).short8Value);
                    continue;
                }
                if(Int.class == type)
                {
                    writeShort(CONST_I);
                    writeInt(((Int) element).intValue);
                    continue;
                }
                if(Int2.class == type)
                {
                    writeShort(CONST_I2);
                    writeInt2(((Int2) element).int2Value);
                    continue;
                }
                if(Int4.class == type)
                {
                    writeShort(CONST_I4);
                    writeInt4(((Int4) element).int4Value);
                    continue;
                }
                if(Int8.class == type)
                {
                    writeShort(CONST_I8);
                    writeInt8(((Int8) element).int8Value);
                    continue;
                }
                if(Long.class == type)
                {
                    writeShort(CONST_J);
                    writeLong(((Long) element).longValue);
                    continue;
                }
                if(Long2.class == type)
                {
                    writeShort(CONST_J2);
                    writeLong2(((Long2) element).long2Value);
                    continue;
                }
                if(Long4.class == type)
                {
                    writeShort(CONST_J4);
                    writeLong4(((Long4) element).long4Value);
                    continue;
                }
                if(Long8.class == type)
                {
                    writeShort(CONST_J8);
                    writeLong8(((Long8) element).long8Value);
                    continue;
                }
                if(String.class == type) with((String) element)
                {
                    writeShort(CONST_ST);
                    writeShort(length);
                    write(toByteArray(charset));
                    continue;
                }
                if(Package.class.isAssignableFrom(type)) with((TableItem) element)
                {
                    writeShort(CONST_PK);
                    writeInt(indexOf(recompilableName));
                    continue;
                }
                if(Type.class.isAssignableFrom(type)) with((TableItem) element)
                {
                    writeShort(CONST_TP);
                    writeInt(indexOf(recompilableName));
                    continue;
                }
                if(Field.class.isAssignableFrom(type)) with((Member) element)
                {
                    writeShort(CONST_FL);
                    writeInt(indexOf(parentType));
                    writeInt(indexOf(recompilableName));
                    continue;
                }
                if(Property.class.isAssignableFrom(type)) with((Member) element)
                {
                    writeShort(CONST_PR);
                    writeInt(indexOf(parentType));
                    writeInt(indexOf(recompilableName));
                    continue;
                }
                if(Callable.class.isAssignableFrom(type)) with((Callable) element, arguments)
                {
                    writeShort(CONST_CL);
                    writeInt(indexOf(parentType));
                    writeInt(indexOf(recompilableName));
                    int alength = length;
                    writeShort(alength);
                    for(int aindex = 0; aindex < alength; aindex++) writeInt(indexOf(operator [](aindex).type));
                    continue;
                }
                throw new UnencodableDataException(String.format(package.getResourceString("not-encodable.constant-type"), new Object[] { type.simpleName }));
            }
        }
        return stream.toByteArray();
    }

    public byte[] serializePackage(Package pack) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_PACK);
            writeShort(pack.attributes);
            writeInt(indexAcquire(pack.specialCanonicalName));
        }
        return stream.toByteArray();
    }

    public byte[] serializeClassType(ClassType type) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_TYPE);
            writeShort(type.attributes);
            writeInt(indexAcquire(type.parentPackage));
            writeInt(indexAcquire(type.specialSimpleName));
            if(type.isHelper())
            {
                writeInt(indexAcquire(type.getHelperForType().specialCanonicalName));
            } else
            {
                ClassType supertype = type.getSuperclassType();
                writeInt(supertype == null ? 0 : indexAcquire(supertype.specialCanonicalName));
                for(int length = type.getSuperservicesLength(), int index = 0; index < length; index++)
                {
                    writeInt(indexAcquire(type.getSuperserviceTypeAt(index).specialCanonicalName));
                }
            }
        }
        return stream.toByteArray();
    }

    public byte[] serializeClassField(Field field) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_CFLD);
            writeShort(field.attributes);
            writeInt(indexAcquire(field.type));
            writeInt(indexAcquire(field.specialSimpleName));
            Constant constant = field.value;
            if(constant != null) writeInt(indexAcquire(constant));
        }
        return stream.toByteArray();
    }

    public byte[] serializeStructField(Field field) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_SFLD);
            writeShort(field.attributes);
            writeInt(indexAcquire(field.type));
            writeInt(indexAcquire(field.specialSimpleName));
            writeInt(field.capacity);
            writeInt(field.structOffset);
        }
        return stream.toByteArray();
    }

    public byte[] serializeProperty(Property property) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_PROP);
            writeShort(property.attributes);
            writeInt(indexAcquire(property.type));
            writeInt(indexAcquire(property.specialSimpleName));
            writeInt(indexAcquire(property.indexMember));
            writeInt(indexAcquire(property.storedMember));
            writeInt(indexAcquire(property.readMember));
            writeInt(indexAcquire(property.writeMember));
        }
        return stream.toByteArray();
    }

    public byte[] serializeCallable(Callable callable) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_OPER);
            writeShort(callable.attributes);
            writeInt(indexAcquire(callable.type));
            writeInt(indexAcquire(callable.specialSimpleName));
            with(callable.arguments)
            {
                int length = length;
                writeShort(length);
                for(int index = 0; index < length; index++) with(operator [](index))
                {
                    writeInt(indexAcquire(type));
                    writeInt(indexAcquire(specialSimpleName));
                }
            }
        }
        return stream.toByteArray();
    }

    public byte[] serializeMethod(Method method) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_METH);
            writeShort(method.attributes);
            writeInt(indexAcquire(method.type));
            writeInt(indexAcquire(method.specialSimpleName));
            with(method.arguments)
            {
                int length = length;
                writeShort(length);
                for(int index = 0; index < length; index++) with(operator [](index))
                {
                    writeInt(indexAcquire(type));
                    writeInt(indexAcquire(specialSimpleName));
                }
            }
            for(int length = method.getThrowablesLength(), int index = 0; index < length; index++) writeInt(indexAcquire(method.getThrowableTypeAt(index).specialCanonicalName));
        }
        return stream.toByteArray();
    }

    public byte[] serializeCode(Code code) throws IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        with((new DataOutputStream(stream)).bigEndianDataOutput)
        {
            writeInt(CHUNK_CODE);
            {
                Hashtable primTable = new Hashtable();
                Hashtable refsTable = new Hashtable();
                StringArray primArray = new StringArray();
                StringArray refsArray = new StringArray();
                with(code.locals) for(int length = length, int index = 0; index < length; index++) with(operator [](index)) if(!isConstant())
                {
                    Type type = type;
                    String name = specialSimpleName;
                    if(type.isPrimitive())
                    {
                        if(!primTable.containsKey(name))
                        {
                            primArray.append(name);
                            primTable[name] = type;
                        }
                        else if(((Type) primTable[name]).fieldWidth < type.fieldWidth)
                        {
                            primTable[name] = type;
                        }
                    } else
                    {
                        if(!refsTable.containsKey(name))
                        {
                            refsArray.append(name);
                            refsTable[name] = type;
                        }
                    }
                }
                int primLength = primArray.length;
                int refsLength = refsArray.length;
                int length = primLength + refsLength;
                if(length + Int.MIN_VALUE > 0xffff + Int.MIN_VALUE)
                {
                    throw new UnencodableDataException(String.format(package.getResourceString("not-encodable.too-namy-locals"), new Object[] { code.parentCallable }));
                }
                writeShort(length);
                for(int index = 0; index < primLength; index++)
                {
                    String name = primArray[index];
                    Type type = (Type) primTable[name];
                    writeInt(indexAcquire(type));
                    writeInt(indexAcquire(name));
                }
                for(int index = 0; index < refsLength; index++)
                {
                    String name = refsArray[index];
                    Type type = (Type) refsTable[name];
                    writeInt(indexAcquire(type));
                    writeInt(indexAcquire(name));
                }
            }
            for(Local thisArgument = code.parentCallable.arguments.thisArgument, int lastIndex = -1, int clength = code.length, int cindex = 0; cindex < clength; cindex++)
            {
                Node node = code[cindex];
                {
                    int lineIndex = (int) node.location;
                    if(lineIndex >= 0 && lineIndex != lastIndex)
                    {
                        int encoded = encodeDeclare(false, lastIndex = lineIndex);
                        if(isLongEncoded(encoded))
                        {
                            writeInt(encoded);
                        } else
                        {
                            writeShort(encoded);
                        }
                    }
                    if((lineIndex = node.debugIndex) >= 0)
                    {
                        int encoded = encodeDeclare(true, ~lineIndex);
                        if(isLongEncoded(encoded))
                        {
                            writeInt(encoded);
                        } else
                        {
                            writeShort(encoded);
                        }
                    }
                    if((lineIndex = node.labelIndex) >= 0)
                    {
                        int encoded = encodeDeclare(true, lineIndex);
                        if(isLongEncoded(encoded))
                        {
                            writeInt(encoded);
                        } else
                        {
                            writeShort(encoded);
                        }
                    }
                }
                int jlength = node.getJumpCasesLength();
                int nodeInstruction = node.instruction;
                int formInstruction = jlength > 0 ? FLAG_BOOLEAN_JUMP : 0;
                switch(nodeInstruction)
                {
                case METHOD_ENTER:
                    writeShort(INST_METHOD_ENTER);
                    writeIndex(((Int) node.reference1).intValue);
                    continue;
                case SWAP:
                    Type type = (Type) node.operand1;
                    writeShort(INST_SWAP | encodeType(type));
                    continue;
                case BOUND:
                case BEGIN:
                case END:
                    writeShort(INST_BOUND);
                    continue;
                case THROW:
                    Object type = node.reference1;
                    writeShort(INST_THROW);
                    writeIndex(indexAcquire(type));
                    continue;
                case LOAD_STATIC:
                    TableItem global = node.operand1;
                    if(node.weak)
                    {
                        writeShort(INST_LOAD_STATICW);
                        writeIndex(indexAcquire(global));
                        continue;
                    }
                    writeShort(formInstruction |= INST_LOAD_STATIC);
                    writeIndex(indexAcquire(global));
                    break;
                case LOAD_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    if(node.weak)
                    {
                        writeShort(INST_LOAD_LOCALW | encodeType(REF));
                        writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                        continue;
                    }
                    writeShort(formInstruction |= INST_LOAD_LOCAL | encodeType(local.type));
                    writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                    break;
                case LOAD_CONST:
                    Constant constant = (Constant) node.operand1;
                    if(constant.isReference())
                    {
                        Object reference = constant.asObject();
                        if(reference == null)
                        {
                            writeShort(INST_LOAD_CONST_NULL);
                            continue;
                        }
                        if(reference instanceof RequiredReflectItem)
                        {
                            writeShort(node.weak ? INST_LOAD_CONSTW : INST_LOAD_CONST);
                            writeIndex(indexAcquire(reference));
                            continue;
                        }
                        if(reference instanceof String)
                        {
                            writeShort(INST_LOAD_CONST);
                            writeIndex(indexAcquire(reference));
                            continue;
                        }
                        throw new UnencodableDataException(String.format(package.getResourceString("not-encodable.constant-value"), new Object[] { reference.getClass().canonicalName }));
                    }
                    Type type = constant.type;
                    if(type.isBoolean())
                    {
                        writeShort(INST_LOAD_CONST_BYTE | (constant.asBoolean() ? 0xff : 0x00));
                        continue;
                    }
                    if(type.isInt())
                    {
                        int value = constant.asInt();
                        if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
                        {
                            writeShort(INST_LOAD_CONST_BYTE | constant.asByte() & 0xff);
                            continue;
                        }
                        if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
                        {
                            writeShort(INST_LOAD_CONST_SHORT);
                            writeShort(value);
                            continue;
                        }
                        writeShort(INST_LOAD_CONST_INT);
                        writeInt(value);
                        continue;
                    }
                    writeShort(INST_LOAD_CONST);
                    writeIndex(indexAcquire(constant));
                    continue;
                case READ_ELEMENT:
                    Type type = (Type) node.operand1;
                    Constant index = (Constant) node.operand2;
                    if(index != null)
                    {
                        writeShort(INST_READ_ELEMENTI | encodeVectorIndex(index.asInt()) | encodeType(type));
                        continue;
                    }
                    writeShort(INST_READ_ELEMENT | encodeType(type));
                    continue;
                case READ_COMPONENT:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_READ_COMPONENT | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand == thisArgument ? 0 : !(operand instanceof Local) ? indexAcquire(operand) : indexOf(operand.specialSimpleName));
                    break;
                case READ_FIELD:
                    TableItem fieldoid = node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_READ_FIELD | encodeOperand(operand));
                    writeIndex(indexAcquire(fieldoid));
                    if(operand != null) writeIndex(operand == thisArgument ? 0 : !(operand instanceof Local) ? indexAcquire(operand) : indexOf(operand.specialSimpleName));
                    break;
                case READ_SPECIAL:
                    TableItem fieldoid = node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_READ_SPECIAL | encodeOperand(operand));
                    writeIndex(indexAcquire(fieldoid));
                    if(operand != null) writeIndex(operand == thisArgument ? 0 : !(operand instanceof Local) ? indexAcquire(operand) : indexOf(operand.specialSimpleName));
                    break;
                case READ_PROPERTY:
                    TableItem fieldoid = node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_READ_PROPERTY | encodeOperand(operand));
                    writeIndex(indexAcquire(fieldoid));
                    if(operand != null) writeIndex(operand == thisArgument ? 0 : !(operand instanceof Local) ? indexAcquire(operand) : indexOf(operand.specialSimpleName));
                    break;
                case STORE_STATIC:
                    TableItem global = node.operand1;
                    Constant constant = (Constant) node.operand2;
                    if(constant != null)
                    {
                        if(constant.isNull())
                        {
                            writeShort(INST_STORE_STATIC_NULL);
                            writeIndex(indexAcquire(global));
                            continue;
                        }
                        Type type = constant.type;
                        if(type.isBoolean())
                        {
                            writeShort(formInstruction |= INST_STORE_STATIC_SHORT);
                            writeIndex(indexAcquire(global));
                            writeShort(constant.asBoolean() ? -1 : 0);
                            break;
                        }
                        if(type.isInt())
                        {
                            int value = constant.asInt();
                            if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
                            {
                                writeShort(INST_STORE_STATIC_SHORT);
                                writeIndex(indexAcquire(global));
                                writeShort(value);
                                continue;
                            }
                            writeShort(INST_STORE_STATIC_INT);
                            writeIndex(indexAcquire(global));
                            writeInt(value);
                            continue;
                        }
                        throw new UnencodableDataException(String.format(package.getResourceString("not-encodable.assignable.global"), new Object[] { type }));
                    }
                    writeShort(formInstruction |= INST_STORE_STATIC);
                    writeIndex(indexAcquire(global));
                    break;
                case STORE_LOCAL:
                case DECLARE_WITH:
                case DECLARE_LOCAL:
                    int store = nodeInstruction - (STORE_LOCAL - STORE_NORMAL);
                    TypedItem local = (TypedItem) node.operand1;
                    Constant constant = (Constant) node.operand2;
                    if(constant != null)
                    {
                        if(constant.isNull())
                        {
                            writeShort(INST_STORE_LOCAL_NULL | encodeStore(store) | encodeType(REF));
                            writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                            continue;
                        }
                        Type type = constant.type;
                        if(type.isBoolean())
                        {
                            writeShort(formInstruction |= INST_STORE_LOCAL_SHORT | encodeStore(store) | encodeType(BOOLEAN));
                            writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                            writeShort(constant.asBoolean() ? -1 : 0);
                            break;
                        }
                        if(type.isInt())
                        {
                            int value = constant.asInt();
                            if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
                            {
                                writeShort(INST_STORE_LOCAL_SHORT | encodeStore(store) | encodeType(type));
                                writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                                writeShort(value);
                                continue;
                            }
                            writeShort(INST_STORE_LOCAL_INT | encodeStore(store) | encodeType(type));
                            writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                            writeInt(value);
                            continue;
                        }
                        throw new UnencodableDataException(String.format(package.getResourceString("not-encodable.assignable.local"), new Object[] { type }));
                    }
                    writeShort(formInstruction |= INST_STORE_LOCAL | encodeStore(store) | encodeType(local.type));
                    writeIndex(local == thisArgument ? 0 : indexOf(local.specialSimpleName));
                    break;
                case INC_PRED_LOAD_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    writeShort(INST_INC_PRED_LOAD_LOCAL | encodeType(local.type));
                    writeIndex(indexOf(local.specialSimpleName));
                    continue;
                case DEC_PRED_LOAD_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    writeShort(INST_DEC_PRED_LOAD_LOCAL | encodeType(local.type));
                    writeIndex(indexOf(local.specialSimpleName));
                    continue;
                case INC_POST_LOAD_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    writeShort(INST_INC_POST_LOAD_LOCAL | encodeType(local.type));
                    writeIndex(indexOf(local.specialSimpleName));
                    continue;
                case DEC_POST_LOAD_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    writeShort(INST_DEC_POST_LOAD_LOCAL | encodeType(local.type));
                    writeIndex(indexOf(local.specialSimpleName));
                    continue;
                case WRITE_COMPONENT:
                    Type type = (Type) node.operand1;
                    writeShort(formInstruction |= INST_WRITE_COMPONENT | encodeType(type));
                    break;
                case WRITE_FIELD:
                    TableItem fieldoid = node.operand1;
                    writeShort(formInstruction |= INST_WRITE_FIELD);
                    writeIndex(indexAcquire(fieldoid));
                    break;
                case WRITE_SPECIAL:
                    TableItem fieldoid = node.operand1;
                    writeShort(formInstruction |= INST_WRITE_SPECIAL);
                    writeIndex(indexAcquire(fieldoid));
                    break;
                case WRITE_PROPERTY:
                    TableItem fieldoid = node.operand1;
                    writeShort(formInstruction |= INST_WRITE_PROPERTY);
                    writeIndex(indexAcquire(fieldoid));
                    break;
                case BRANCH:
                    if(jlength > 0)
                    {
                        int min = node.getJumpCaseValueAt(0);
                        int max = min;
                        for(int jindex = 1; jindex < jlength; jindex++)
                        {
                            int cur = node.getJumpCaseValueAt(jindex);
                            if(min > cur) min = cur;
                            if(max < cur) max = cur;
                        }
                        Node jumpDefault = node.jumpDefault;
                        if(((long) max - min + 2) <= ((long) jlength << 1))
                        {
                            writeShort(INST_SWITCH_TABLE);
                            writeInt(min);
                            writeIndex(jlength = max - min + 1);
                            for(int jvalue = min, int jindex = 0; jindex < jlength; jvalue++, jindex++)
                            {
                                Node target = node.getJumpCase(jvalue);
                                if(target == null) target = jumpDefault;
                                writeLabel(target);
                            }
                        } else
                        {
                            writeShort(INST_SWITCH_LOOKUP);
                            writeIndex(jlength);
                            for(int jindex = 0; jindex < jlength; jindex++)
                            {
                                int jvalue = node.getJumpCaseValueAt(jindex);
                                Node target = node.getJumpCaseNodeAt(jindex);
                                writeInt(jvalue);
                                writeLabel(target);
                            }
                        }
                        writeLabel(jumpDefault);
                        continue;
                    }
                    /* падение через */
                case JUMP:
                case RETURN:
                    writeShort(INST_JUMP);
                    writeLabel(node.jumpDefault);
                    continue;
                case LOAD_INTERRUPT:
                    TableItem callable = node.operand1;
                    writeShort(INST_LOAD_INTERRUPT);
                    writeIndex(indexAcquire(callable));
                    continue;
                case REF_EQ:
                    TableItem operand = node.operand1;
                    writeShort(formInstruction |= (node.weak ? INST_REF_EQW : INST_REF_EQ) | encodeOperand(operand) | encodeType(REF));
                    if(operand != null) writeIndex(operand == thisArgument ? 0 : !(operand instanceof Local) ? indexAcquire(operand) : indexOf(operand.specialSimpleName));
                    break;
                case REF_NE:
                    TableItem operand = node.operand1;
                    writeShort(formInstruction |= (node.weak ? INST_REF_NEW : INST_REF_NE) | encodeOperand(operand) | encodeType(REF));
                    if(operand != null) writeIndex(operand == thisArgument ? 0 : !(operand instanceof Local) ? indexAcquire(operand) : indexOf(operand.specialSimpleName));
                    break;
                case REF_IS_NULL:
                    writeShort(formInstruction |= (node.weak ? INST_REF_IS_NULLW : INST_REF_IS_NULL) | encodeType(REF));
                    break;
                case REF_IS_OBJECT:
                    writeShort(formInstruction |= (node.weak ? INST_REF_IS_OBJECTW : INST_REF_IS_OBJECT) | encodeType(REF));
                    break;
                case CLINIT_TRY_LOCK:
                    TableItem operand = node.operand1;
                    writeShort(formInstruction |= INST_CLINIT_TRY_LOCK);
                    writeIndex(indexAcquire(operand));
                    break;
                case CLINIT_UNLOCK:
                    TableItem operand = node.operand1;
                    writeShort(INST_CLINIT_UNLOCK);
                    writeIndex(indexAcquire(operand));
                    continue;
                case MONITOR_ENTER:
                    writeShort(INST_MONITOR_ENTER | encodeType(REF));
                    continue;
                case MONITOR_LEAVE:
                    writeShort(INST_MONITOR_LEAVE | encodeType(REF));
                    continue;
                case FINALLY_ENTER:
                    writeShort(INST_FINALLY_ENTER);
                    continue;
                case FINALLY_LEAVE:
                    writeShort(INST_FINALLY_LEAVE);
                    continue;
                case NEW_ARRAY:
                    TableItem type = node.operand1;
                    Constant length = (Constant) node.operand2;
                    writeShort(INST_NEW_ARRAY);
                    writeIndex(indexAcquire(type));
                    writeInt(length.asInt());
                    continue;
                case INVOKE_FINALLY:
                    Node target = (Node) node.reference1;
                    writeShort(INST_INVOKE_FINALLY);
                    writeLabel(target);
                    continue;
                case INVOKE_STATIC:
                    TableItem callable = node.operand1;
                    writeShort(formInstruction |= node.weak ? INST_INVOKE_STATICD : INST_INVOKE_STATIC);
                    writeIndex(indexAcquire(callable));
                    break;
                case INVOKE_SPECIAL:
                    TableItem callable = node.operand1;
                    writeShort(formInstruction |= node.weak ? INST_INVOKE_SPECIALD : INST_INVOKE_SPECIAL);
                    writeIndex(indexAcquire(callable));
                    break;
                case INVOKE_VIRTUAL:
                    TableItem callable = node.operand1;
                    writeShort(formInstruction |= node.weak ? INST_INVOKE_VIRTUALD : INST_INVOKE_VIRTUAL);
                    writeIndex(indexAcquire(callable));
                    break;
                case INVOKE_SERVICE:
                    TableItem callable = node.operand1;
                    writeShort(formInstruction |= node.weak ? INST_INVOKE_SERVICED : INST_INVOKE_SERVICE);
                    writeIndex(indexAcquire(callable));
                    break;
                case O_VECT_DIV:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_DIV | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_PCK:
                    Type type = (Type) node.operand1;
                    writeShort(INST_VECT_PCK | encodeType(type));
                    continue;
                case O_VECT_LUP:
                    Type type = (Type) node.operand1;
                    writeShort(INST_VECT_LUP | encodeType(type));
                    continue;
                case O_VECT_UUP:
                    Type type = (Type) node.operand1;
                    writeShort(INST_VECT_UUP | encodeType(type));
                    continue;
                case O_VECT_MUL:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_MUL | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SHL:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SHL | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SHR:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SHR | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SHRU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SHRU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_ADD:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_ADD | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SUB:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SUB | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_EQ:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_EQ | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_NE:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_NE | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_GT:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_GT | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_GE:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_GE | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_LT:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_LT | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_LE:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_LE | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_DIV:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_DIV | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_DIVU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_DIVU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_REM:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_REM | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_REMU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_REMU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_MUL:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_MUL | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_SHL:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_SHL | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_SHR:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_SHR | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_SHRU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_SHRU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_ADD:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_ADD | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_SUB:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_SCAL_SUB | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_EQ:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_SCAL_EQ | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_SCAL_NE:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_SCAL_NE | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_SCAL_GT:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_SCAL_GT | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_SCAL_GE:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_SCAL_GE | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_SCAL_LT:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_SCAL_LT | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_SCAL_LE:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_SCAL_LE | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_VECT_NEG:
                    Type type = (Type) node.operand1;
                    writeShort(INST_VECT_NEG | encodeType(type));
                    continue;
                case CAST_TO:
                    TableItem newType = node.operand1;
                    Type oldType = (Type) node.operand2;
                    writeShort(INST_CAST_TO | encodeType(oldType));
                    writeIndex(indexAcquire(newType));
                    continue;
                case INC_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    writeShort(INST_INC_LOCAL | encodeType(local.type));
                    writeIndex(indexOf(local.specialSimpleName));
                    continue;
                case DEC_LOCAL:
                    TypedItem local = (TypedItem) node.operand1;
                    writeShort(INST_DEC_LOCAL | encodeType(local.type));
                    writeIndex(indexOf(local.specialSimpleName));
                    continue;
                case O_BIT_NOT:
                    Type type = (Type) node.operand1;
                    writeShort(formInstruction |= INST_BIT_NOT | encodeType(type));
                    break;
                case O_BIT_AND:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_BIT_AND | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_BIT_OR:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_BIT_OR | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case O_BIT_XOR:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_BIT_XOR | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case DUP1:
                    Type type = (Type) node.operand1;
                    writeShort(INST_DUP1 | encodeType(type));
                    continue;
                case DUP2:
                    Type type = (Type) node.operand1;
                    writeShort(INST_DUP2 | encodeType(type));
                    continue;
                case O_VECT_HMUL:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_HMUL | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_HMULU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_HMULU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SADD:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SADD | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SADDU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SADDU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SSUB:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SSUB | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_VECT_SSUBU:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(INST_VECT_SSUBU | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    continue;
                case O_SCAL_NEG:
                    Type type = (Type) node.operand1;
                    writeShort(INST_SCAL_NEG | encodeType(type));
                    continue;
                case INSTANCEOF:
                    TableItem type = node.operand1;
                    writeShort(formInstruction |= INST_INSTANCE_OF | encodeType(REF));
                    writeIndex(indexAcquire(type));
                    break;
                case TEST_EQZ:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_TEST_EQZ | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case TEST_NEZ:
                    Type type = (Type) node.operand1;
                    TableItem operand = node.operand2;
                    writeShort(formInstruction |= INST_TEST_NEZ | encodeOperand(operand) | encodeType(type));
                    if(operand != null) writeIndex(operand instanceof Local ? indexOf(operand.specialSimpleName) : indexAcquire(operand));
                    break;
                case NEW_VECTOR:
                    Type type = (Type) node.operand1;
                    writeShort(INST_NEW_VECTOR | encodeType(type));
                    continue;
                case NEW_INSTANCE:
                    TableItem type = node.operand1;
                    writeShort(INST_NEW_INSTANCE);
                    writeIndex(indexAcquire(type));
                    continue;
                case NEW_ARRAY_MONO:
                    TableItem type = node.operand1;
                    writeShort(INST_NEW_ARRAY_MONO);
                    writeIndex(indexAcquire(type));
                    continue;
                case NEW_ARRAY_MULTI:
                    TableItem type = node.operand1;
                    writeShort(INST_NEW_ARRAY_MULTI);
                    writeIndex(indexAcquire(type));
                    continue;
                case DUP1X1:
                    Type type = (Type) node.operand1;
                    writeShort(INST_DUP1X1 | encodeType(type));
                    continue;
                case DUP1X2:
                    Type type = (Type) node.operand1;
                    writeShort(INST_DUP1X2 | encodeType(type));
                    continue;
                case METHOD_LEAVE:
                    writeShort(INST_METHOD_LEAVE);
                    continue;
                case EXCEPT_ENTER:
                    writeShort(INST_EXCEPT_ENTER);
                    continue;
                case EXCEPT_TRY:
                    Node begin = (Node) node.reference1;
                    Node end = (Node) node.reference2;
                    writeShort(INST_EXCEPT_TRY);
                    writeLabel(begin);
                    writeLabel(end);
                    continue;
                case EXCEPT_CATCH:
                    Node block = (Node) node.reference1;
                    Object type = node.reference2;
                    writeShort(INST_EXCEPT_CATCH);
                    writeLabel(block);
                    writeIndex(indexAcquire(type));
                    continue;
                case EXCEPT_FINALLY:
                    Node block = (Node) node.reference1;
                    writeShort(INST_EXCEPT_FINALLY);
                    writeLabel(block);
                    continue;
                case EXCEPT_LEAVE:
                    writeShort(INST_EXCEPT_LEAVE);
                    continue;
                default:
                    throw new UnencodableDataException(String.format(package.getResourceString("not-encodable.instruction"), new Object[] { Int.toHexString(nodeInstruction) }));
                }
                if((formInstruction & FLAG_BOOLEAN_JUMP) != 0)
                {
                    writeLabel(node.jumpIsTrue);
                    writeLabel(node.jumpIsFalse);
                }
            }
        }
        return stream.toByteArray();
    }

    protected void afterConstruction() { operator +(null); }

    private int indexAcquire(Object element) throws IOException {
        if(element instanceof Constant)
        {
            Constant constant = (Constant) element;
            if(constant.isReference())
            {
                element = constant.asObject();
            }
            else if(constant.isBoolean())
            {
                element = constant.asBoolean() ? Boolean.TRUE : Boolean.FALSE;
            }
            else switch(constant.type.kind)
            {
            case CHAR:
                element = new Char(constant.asChar());
                break;
            case REAL:
                element = new Real(constant.asReal());
                break;
            case DOUBLE:
                element = new Double(constant.asDouble());
                break;
            case DOUBLE2:
                element = new Double2(constant.asDouble2());
                break;
            case DOUBLE4:
                element = new Double4(constant.asDouble4());
                break;
            case DOUBLE8:
                element = new Double8(constant.asDouble8());
                break;
            case FLOAT:
                element = new Float(constant.asFloat());
                break;
            case FLOAT2:
                element = new Float2(constant.asFloat2());
                break;
            case FLOAT4:
                element = new Float4(constant.asFloat4());
                break;
            case FLOAT8:
                element = new Float8(constant.asFloat8());
                break;
            case BYTE:
                element = new Byte(constant.asByte());
                break;
            case BYTE2:
                element = new Byte2(constant.asByte2());
                break;
            case BYTE4:
                element = new Byte4(constant.asByte4());
                break;
            case BYTE8:
                element = new Byte8(constant.asByte8());
                break;
            case SHORT:
                element = new Short(constant.asShort());
                break;
            case SHORT2:
                element = new Short2(constant.asShort2());
                break;
            case SHORT4:
                element = new Short4(constant.asShort4());
                break;
            case SHORT8:
                element = new Short8(constant.asShort8());
                break;
            case INT:
                element = new Int(constant.asInt());
                break;
            case INT2:
                element = new Int2(constant.asInt2());
                break;
            case INT4:
                element = new Int4(constant.asInt4());
                break;
            case INT8:
                element = new Int8(constant.asInt8());
                break;
            case LONG:
                element = new Long(constant.asLong());
                break;
            case LONG2:
                element = new Long2(constant.asLong2());
                break;
            case LONG4:
                element = new Long4(constant.asLong4());
                break;
            case LONG8:
                element = new Long8(constant.asLong8());
            }
        }
        int result = indexOf(element);
        if(result < 0)
        {
            if(element instanceof String && ((String) element).length > 0xffff)
            {
                throw new UnencodableDataException(package.getResourceString("not-encodable.too-long-string"));
            }
            label0:
            {
                if(element instanceof RequiredReflectItem) with((TableItem) element)
                {
                    indexAcquire(recompilableName);
                    break label0;
                }
                if(element instanceof Fieldoid) with((Member) element)
                {
                    indexAcquire(parentType);
                    indexAcquire(recompilableName);
                    break label0;
                }
                if(element instanceof Callable) with((Callable) element, arguments)
                {
                    indexAcquire(parentType);
                    indexAcquire(recompilableName);
                    for(int length = length, int index = 0; index < length; index++) indexAcquire(operator [](index).type);
                }
            }
            result = operator +(element).length - 1;
        }
        return result;
    }
}

helper BinarySourceGeneratorCodeDataOutput: DataOutput
{
    public void writeIndex(int index) throws IOException {
        if(index <= Short.MAX_VALUE)
        {
            writeShort(index);
        } else
        {
            writeInt(Int.MIN_VALUE | index);
        }
    }

    public void writeLabel(Node target) throws IOException {
        int index;
        int encoded = AVTOORecompilable.encodeNext();
        if((index = target.debugIndex) >= 0)
        {
            encoded = AVTOORecompilable.encodeUsing(~index);
        }
        else if((index = target.labelIndex) >= 0)
        {
            encoded = AVTOORecompilable.encodeUsing(index);
        }
        if(AVTOORecompilable.isLongEncoded(encoded))
        {
            writeInt(encoded);
        } else
        {
            writeShort(encoded);
        }
    }
}