ru.malik.elaborarer.avt.generator.pas

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

{
    Этот исходный код является частью проекта ПВТ-ОО.

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

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

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

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

unit ru.malik.elaborarer.avt.generator;

{$MODE DELPHI}

interface

uses
    pascalx.lang,
    pascalx.io,
    pascalx.io.vfs,
    pascalx.osapi,
    pascalx.utils,
    ru.malik.elaborarer.avt.programme,
    ru.malik.elaborarer.avt.tree;

{$ASMMODE INTEL,CALLING REGISTER,TYPEINFO ON}

{%region pulic }
type
    AVTCodePrinter = class;
    AVTCodeGenerator = class;

    AVTCodePrinter = class(_Object, Closeable, Output)
    private
        class function spaces(count: int): AnsiString; static;
    private
        isLabelPrinted: boolean;
        stream: Output;
        procedure writeString(const data: AnsiString);
    public
        constructor create(stream: Output);
        procedure close();
        procedure flush();
        procedure write(data: int); overload;
        procedure write(const src: byte_Array1d); overload;
        procedure write(const src: byte_Array1d; offset, length: int); overload;
        procedure print(const data: AnsiString);
        { На будущее: добавить возможность вывода данных с произвольным отступом слева (сейчас этот отступ фиксирован). }
        { (Отступ измеряется в пробелах) }
        procedure printLabel(const name: AnsiString);
        procedure printHeader(const data: AnsiString);
        procedure printInstruction(const data: AnsiString); overload;
        procedure printInstruction(const instr: AnsiString; const operands: array of AnsiString); overload;
        procedure printContinuation(const data: AnsiString);
        procedure println(); overload;
        procedure println(const data: AnsiString); overload;
        procedure printlnHeader(const data: AnsiString);
        procedure printlnSourceLine(const data: AnsiString);
        procedure printlnInstruction(const data: AnsiString); overload;
        procedure printlnInstruction(const instr: AnsiString; const operands: array of AnsiString); overload;
        procedure printlnContinuation(const data: AnsiString);
    end;

    AVTCodeGenerator = class(AVTCodeBuilder)
    private
        class procedure clearDirectory(vfs: WriteableVirtualFileSystem; const dir: UnicodeString); static;
        class procedure copyFile(vfs: WriteableVirtualFileSystem; const srcFile, dstFile: UnicodeString); static;
        class procedure copyFilesOnly(vfs: WriteableVirtualFileSystem; const srcDir, dstDir: UnicodeString); static;
        class procedure copyData(src: Input; dst: Output); static;
        class procedure compileImplementors(typeRef, serviceRef: AVTTypeStructured; printer: AVTCodePrinter); static;
        class procedure listResourcesInDir(vfs: WriteableVirtualFileSystem; const dir, subDir: UnicodeString; table: Hashtable); static;
        class function listResources(vfs: WriteableVirtualFileSystem; const dir: UnicodeString): Hashtable; static;
        class function resourceDirToDir(const dir: UnicodeString): UnicodeString; static;
        class function resourceDirToPath(const dir: UnicodeString): AnsiString; static;
        class function labelNumberToString(prefix: char; labelNumber: int): AnsiString; overload; static;
        class function labelNumberToString(labelNumber: int): AnsiString; overload; static;
        class function typeNameToSourceName(typeRef: AVTType): AnsiString; static;
        class function typeReturnSuffix(typeRef: AVTType): AnsiString; static;
        class function avtBooleanValueToString(value: boolean): AnsiString; static;
        class function avtIntValueToString(value: int): AnsiString; static;
        class function avtByteToString(value: int): AnsiString; static;
        class function avtShortToString(value: int): AnsiString; static;
        class function avtIntToString(value: int): AnsiString; static;
        class function avtLongToString(value: long): AnsiString; static;
        class function typeToInstructionName(typeRef: AVTType): AnsiString; static;
        class function getImplementorsVector(typeRef: AVTTypeStructured): Vector; static;
    private
        i2: AVTInt2Storage;
        i4: AVTInt4Storage;
        i8: AVTInt8Storage;
        l: AVTLongStorage;
        l2: AVTLong2Storage;
        l4: AVTLong4Storage;
        l8: AVTLong8Storage;
        f: AVTFloatStorage;
        f2: AVTFloat2Storage;
        f4: AVTFloat4Storage;
        f8: AVTFloat8Storage;
        d: AVTDoubleStorage;
        d2: AVTDouble2Storage;
        d4: AVTDouble4Storage;
        d8: AVTDouble8Storage;
        b2: AVTByte2Storage;
        b4: AVTByte4Storage;
        b8: AVTByte8Storage;
        s2: AVTShort2Storage;
        s4: AVTShort4Storage;
        s8: AVTShort8Storage;
        x: AVTRealStorage;
        strings: AVTStringStorage;
        debugInfoSources: AVTStringStorage;
        debugInfoStrings: AVTStringStorage;
        procedure compileProgramme(vfs: WriteableVirtualFileSystem; const progFile: UnicodeString; packList: Vector; resList: Hashtable);
        procedure compileProgrammeConstants(vfs: WriteableVirtualFileSystem; const constFile: UnicodeString);
        procedure compileProgrammeDebugInfo(vfs: WriteableVirtualFileSystem; const dbinfDir: UnicodeString);
        procedure compilePackage(currPackRef, nextPackRef: AVTPackage; vfs: WriteableVirtualFileSystem; const codeDir, dataDir, nativesDir: UnicodeString);
        procedure compilePackageData(currPackRef, nextPackRef: AVTPackage; vfs: WriteableVirtualFileSystem; const dataFile: UnicodeString);
        procedure compilePrimitiveData(typeRef: AVTTypePrimitive; vfs: WriteableVirtualFileSystem; const dataFile: UnicodeString);
        procedure compileClassCode(typeRef: AVTTypeStructured; vfs: WriteableVirtualFileSystem; const codeFile, nativesFile: UnicodeString);
        procedure compileClassData(typeRef: AVTTypeStructured; vfs: WriteableVirtualFileSystem; const dataFile: UnicodeString);
        procedure compileMethod(methodRef: AVTMethod; printer: AVTCodePrinter);
        procedure compileInstructionNode(node: AVTNode; methodRef: AVTMethod; printer: AVTCodePrinter);
        function nonStackableTypeToStackable(typeRef: AVTType): AVTType;
    public
        constructor create(programme: AVTProgramme);
        destructor destroy; override;
        function generate(vfs: WriteableVirtualFileSystem; const directory: UnicodeString): UnicodeString;
    end;
{%endregion}

implementation

{%region AVTCodePrinter }
    class function AVTCodePrinter.spaces(count: int): AnsiString;
    var
        i: int;
        s: AnsiString;
    begin
        s := AnsiString_create(count);
        for i := 0 to count - 1 do begin
            s[i + 1] := #$20;
        end;
        result := s;
    end;

    procedure AVTCodePrinter.writeString(const data: AnsiString);
    begin
        stream.write(stringToByteArray(data));
    end;

    constructor AVTCodePrinter.create(stream: Output);
    begin
        inherited create();
        self.stream := stream;
    end;

    procedure AVTCodePrinter.close();
    begin
        stream.close();
        free();
    end;

    procedure AVTCodePrinter.flush();
    begin
        stream.flush();
    end;

    procedure AVTCodePrinter.write(data: int);
    begin
        stream.write(data);
    end;

    procedure AVTCodePrinter.write(const src: byte_Array1d);
    begin
        stream.write(src);
    end;

    procedure AVTCodePrinter.write(const src: byte_Array1d; offset, length: int);
    begin
        stream.write(src, offset, length);
    end;

    procedure AVTCodePrinter.print(const data: AnsiString);
    begin
        isLabelPrinted := false;
        writeString(data);
    end;

    procedure AVTCodePrinter.printLabel(const name: AnsiString);
    begin
        if isLabelPrinted then begin
            writeString(LINE_ENDING + '        ' + name + ':' + spaces(intMax(7 - length(name), 1)));
            exit;
        end;
        isLabelPrinted := true;
        writeString('        ' + name + ':' + spaces(intMax(7 - length(name), 1)));
    end;

    procedure AVTCodePrinter.printHeader(const data: AnsiString);
    begin
        if isLabelPrinted then begin
            isLabelPrinted := false;
            writeString(LINE_ENDING + '    ' + data + LINE_ENDING);
            exit;
        end;
        writeString('    ' + data);
    end;

    procedure AVTCodePrinter.printInstruction(const data: AnsiString);
    begin
        if isLabelPrinted then begin
            isLabelPrinted := false;
            writeString(data);
            exit;
        end;
        writeString('                ' + data);
    end;

    procedure AVTCodePrinter.printInstruction(const instr: AnsiString; const operands: array of AnsiString);
    var
        i: int;
        lim: int;
        data: AnsiString;
    begin
        lim := length(operands) - 1;
        if lim < 0 then begin
            data := instr;
        end else begin
            data := instr + spaces(intMax(8 - length(instr), 1));
        end;
        for i := 0 to lim do begin
            data := data + operands[i];
            if i < lim then data := data + ', ';
        end;
        printInstruction(data);
    end;

    procedure AVTCodePrinter.printContinuation(const data: AnsiString);
    begin
        isLabelPrinted := false;
        writeString('                        ' + data);
    end;

    procedure AVTCodePrinter.println();
    begin
        isLabelPrinted := false;
        writeString(LINE_ENDING);
    end;

    procedure AVTCodePrinter.println(const data: AnsiString);
    begin
        isLabelPrinted := false;
        writeString(data + LINE_ENDING);
    end;

    procedure AVTCodePrinter.printlnHeader(const data: AnsiString);
    begin
        if isLabelPrinted then begin
            isLabelPrinted := false;
            writeString(LINE_ENDING + '    ' + data + LINE_ENDING);
            exit;
        end;
        writeString('    ' + data + LINE_ENDING);
    end;

    procedure AVTCodePrinter.printlnSourceLine(const data: AnsiString);
    begin
        if isLabelPrinted then begin
            isLabelPrinted := false;
            writeString(LINE_ENDING + '        ; ' + data + LINE_ENDING);
            exit;
        end;
        writeString('        ; ' + data + LINE_ENDING);
    end;

    procedure AVTCodePrinter.printlnInstruction(const data: AnsiString);
    begin
        if isLabelPrinted then begin
            isLabelPrinted := false;
            writeString(data + LINE_ENDING);
            exit;
        end;
        writeString('                ' + data + LINE_ENDING);
    end;

    procedure AVTCodePrinter.printlnInstruction(const instr: AnsiString; const operands: array of AnsiString);
    var
        i: int;
        lim: int;
        data: AnsiString;
    begin
        lim := length(operands) - 1;
        if lim < 0 then begin
            data := instr;
        end else begin
            data := instr + spaces(intMax(8 - length(instr), 1));
        end;
        for i := 0 to lim do begin
            data := data + operands[i];
            if i < lim then data := data + ', ';
        end;
        printlnInstruction(data);
    end;

    procedure AVTCodePrinter.printlnContinuation(const data: AnsiString);
    begin
        isLabelPrinted := false;
        writeString('                        ' + data + LINE_ENDING);
    end;
{%endregion}

{%region AVTCodeGenerator }
    class procedure AVTCodeGenerator.clearDirectory(vfs: WriteableVirtualFileSystem; const dir: UnicodeString);
    var
        dirs: Vector;
        files: Vector;
        enum: FileEnumeration;
        name: UnicodeString;
    begin
        dirs := nil;
        files := nil;
        try
            enum := vfs.findFirst(dir);
            if enum <> nil then begin
                try
                    repeat
                        if enum.isDirectory() then begin
                            if dirs = nil then dirs := Vector.create();
                            dirs.append(ValueOfUnicodeString.create(enum.getName()));
                        end else begin
                            if files = nil then files := Vector.create();
                            files.append(ValueOfUnicodeString.create(enum.getName()));
                        end;
                    until not enum.findNext();
                finally
                    enum.close();
                end;
                if files <> nil then with files.elements() do while hasMoreElements() do begin
                    vfs.deleteFile(dir + nextElement().unicodeStringValue());
                end;
                if dirs <> nil then with dirs.elements() do while hasMoreElements() do begin
                    name := dir + nextElement().unicodeStringValue();
                    clearDirectory(vfs, name + '/');
                    vfs.deleteDirectory(name);
                end;
            end;
        finally
            dirs.free();
            files.free();
        end;
    end;

    class procedure AVTCodeGenerator.copyFile(vfs: WriteableVirtualFileSystem; const srcFile, dstFile: UnicodeString);
    var
        src: Input;
        dst: Output;
    begin
        src := vfs.openFileForReading(srcFile);
        try
            dst := vfs.createFile(dstFile);
            try
                copyData(src, dst);
            finally
                dst.close();
            end;
        finally
            src.close();
        end;
    end;

    class procedure AVTCodeGenerator.copyFilesOnly(vfs: WriteableVirtualFileSystem; const srcDir, dstDir: UnicodeString);
    var
        name: UnicodeString;
        enum: FileEnumeration;
    begin
        enum := vfs.findFirst(srcDir);
        if enum <> nil then begin
            try
                repeat
                    if not enum.isDirectory() then begin
                        name := enum.getName();
                        copyFile(vfs, srcDir + name, dstDir + name);
                    end;
                until not enum.findNext();
            finally
                enum.close();
            end;
        end;
    end;

    class procedure AVTCodeGenerator.copyData(src: Input; dst: Output);
    var
        length: int;
        buffer: byte_Array1d;
    begin
        buffer := byte_Array1d_create($0800);
        repeat
            length := src.read(buffer);
            if length <= 0 then break;
            dst.write(buffer, 0, length);
        until false;
    end;

    class procedure AVTCodeGenerator.compileImplementors(typeRef, serviceRef: AVTTypeStructured; printer: AVTCodePrinter);
    label
         label0;
    var
        i: int;
        vindex: int;
        methodName: AnsiString;
        classMethod: AVTMethod;
        serviceMethod: AVTMethod;
        classRef: AVTTypeStructured;
        found: TObject;
    begin
        printer.println();
        printer.printlnHeader('impl ' + typeRef.fasmArgumentName + ', ' + serviceRef.fasmArgumentName + ' ; <fold ' + serviceRef.fullName + '>');
        for i := 0 to serviceRef.serviceMethodsCount - 1 do begin
            classMethod := nil;
            serviceMethod := serviceRef.serviceMethod[i];
            methodName := serviceMethod.simpleName;
            classRef := typeRef;
            repeat
                with classRef.members(methodName) do while hasMoreElements() do begin
                    found := nextElement().objectValue();
                    if (found is AVTMethod) and ((found = serviceMethod) or AVTMethod(found).isOverride(serviceMethod)) then begin
                        classMethod := AVTMethod(found);
                        goto label0;
                    end;
                end;
                classRef := classRef.extends();
            until classRef = nil;
            with typeRef.implements() do while hasMoreElements() do begin
                classRef := nextElement().objectValue() as AVTTypeStructured;
                with classRef.members(methodName) do while hasMoreElements() do begin
                    found := nextElement().objectValue();
                    if (found is AVTMethod) and ((found = serviceMethod) or AVTMethod(found).isOverride(serviceMethod)) then begin
                        classMethod := AVTMethod(found);
                        goto label0;
                    end;
                end;
            end;
            label0:
            vindex := classMethod.virtualIndex;
            if vindex >= 0 then begin
                printer.printlnInstruction('jumpvirtual', [classMethod.fasmFullName, avtIntToString(vindex)]);
            end else begin
                printer.printlnInstruction('jumpspecial', [classMethod.fasmFullName]);
            end;
        end;
        printer.printlnHeader('; </fold>');
    end;

    class procedure AVTCodeGenerator.listResourcesInDir(vfs: WriteableVirtualFileSystem; const dir, subDir: UnicodeString; table: Hashtable);
    var
        name: UnicodeString;
        list: Vector;
        enum: FileEnumeration;
    begin
        list := nil;
        enum := vfs.findFirst(dir + subDir);
        if enum <> nil then begin
            try
                repeat
                    name := enum.getName();
                    if enum.isDirectory() then begin
                        listResourcesInDir(vfs, dir, subDir + name + '/', table);
                    end else
                    if not stringEndsWith(UnicodeString('.avt'), stringToLowerCase(name)) then begin
                        if list = nil then begin
                            list := Vector.create();
                            table.put(ValueOfUnicodeString.create(subDir), ValueOfObject.create(list));
                        end;
                        list.append(ValueOfUnicodeString.create(name));
                    end;
                until not enum.findNext();
            finally
                enum.close();
            end;
        end;
    end;

    class function AVTCodeGenerator.listResources(vfs: WriteableVirtualFileSystem; const dir: UnicodeString): Hashtable;
    var
        table: Hashtable;
    begin
        table := Hashtable.create();
        listResourcesInDir(vfs, dir, '', table);
        result := table;
    end;

    class function AVTCodeGenerator.resourceDirToDir(const dir: UnicodeString): UnicodeString;
    var
        len: int;
    begin
        len := length(dir);
        if len <= 0 then begin
            result := '';
            exit;
        end;
        result := stringReplace(stringCopy(dir, 1, len), '/', '.');
    end;

    class function AVTCodeGenerator.resourceDirToPath(const dir: UnicodeString): AnsiString;
    begin
        result := stringToUTF8(UnicodeString('/') + stringReplace(dir, '.', '/'));
    end;

    class function AVTCodeGenerator.labelNumberToString(prefix: char; labelNumber: int): AnsiString;
    begin
        if (labelNumber >= 0) and (labelNumber <= $0fff) then begin
            result := '.' + prefix + '.' + stringToUpperCase(intToHexString((labelNumber shr 8) and $0f) + intToHexString((labelNumber shr 4) and $0f) + intToHexString(labelNumber and $0f));
            exit;
        end;
        result := '.' + prefix + '.' + stringToUpperCase(intToHexString(labelNumber));
    end;

    class function AVTCodeGenerator.labelNumberToString(labelNumber: int): AnsiString;
    begin
        result := labelNumberToString('L', labelNumber);
    end;

    class function AVTCodeGenerator.typeNameToSourceName(typeRef: AVTType): AnsiString;
    var
        dim: int;
    begin
        if typeRef is AVTTypeArray then begin
            dim := AVTTypeArray(typeRef).dimensionsCount;
            result := AVTTypeArray(typeRef).cellType.simpleName + '.' + char(int('0') + (dim div 10)) + char(int('0') + (dim mod 10)) + 'd.inc';
            exit;
        end;
        result := typeRef.simpleName + '.inc';
    end;

    class function AVTCodeGenerator.typeReturnSuffix(typeRef: AVTType): AnsiString;
    begin
        if typeRef.isVoid() then begin
            result := '';
            exit;
        end;
        if typeRef.isDword() then begin
            result := 'd';
            exit;
        end;
        if typeRef.isQword() then begin
            result := 'q';
            exit;
        end;
        if typeRef.isTword() then begin
            result := 't';
            exit;
        end;
        if typeRef.isXword() then begin
            result := 'x';
            exit;
        end;
        if typeRef.isYword() then begin
            result := 'y';
            exit;
        end;
        if typeRef.isZword() then begin
            result := 'z';
            exit;
        end;
        result := 'r';
    end;

    class function AVTCodeGenerator.avtBooleanValueToString(value: boolean): AnsiString;
    begin
        if value then begin
            result := 'true';
            exit;
        end;
        result := 'false';
    end;

    class function AVTCodeGenerator.avtIntValueToString(value: int): AnsiString;
    begin
        if value >= 0 then begin
            result := avtIntToString(value);
            exit;
        end;
        result := '-' + avtIntToString(-value);
    end;

    class function AVTCodeGenerator.avtByteToString(value: int): AnsiString;
    var
        i: int;
        s: AnsiString;
    begin
        s := intToHexString(value and $ff);
        for i := length(s) + 1 to 2 do s := '0' + s;
        result := '$' + s;
    end;

    class function AVTCodeGenerator.avtShortToString(value: int): AnsiString;
    var
        i: int;
        s: AnsiString;
    begin
        s := intToHexString(value and $ffff);
        for i := length(s) + 1 to 4 do s := '0' + s;
        result := '$' + s;
    end;

    class function AVTCodeGenerator.avtIntToString(value: int): AnsiString;
    var
        i: int;
        s: AnsiString;
    begin
        s := intToHexString(value);
        for i := length(s) + 1 to 8 do s := '0' + s;
        result := '$' + s;
    end;

    class function AVTCodeGenerator.avtLongToString(value: long): AnsiString;
    var
        i: int;
        s: AnsiString;
    begin
        s := longToHexString(value);
        for i := length(s) + 1 to 16 do s := '0' + s;
        result := '$' + s;
    end;

    class function AVTCodeGenerator.typeToInstructionName(typeRef: AVTType): AnsiString;
    begin
        if typeRef is AVTTypeStructured then begin
            result := 'r';
            exit;
        end;
        if typeRef is AVTTypePrimitive then begin
            case AVTTypePrimitive(typeRef).primitiveKind of
            AVT_CHAR:    result := 'c';
            AVT_REAL:    result := 'x';
            AVT_BOOLEAN,
            AVT_BYTE:    result := 'b';
            AVT_BYTE2:   result := 'b2';
            AVT_BYTE4:   result := 'b4';
            AVT_BYTE8:   result := 'b8';
            AVT_SHORT:   result := 's';
            AVT_SHORT2:  result := 's2';
            AVT_SHORT4:  result := 's4';
            AVT_SHORT8:  result := 's8';
            AVT_INT:     result := 'i';
            AVT_INT2:    result := 'i2';
            AVT_INT4:    result := 'i4';
            AVT_INT8:    result := 'i8';
            AVT_LONG:    result := 'l';
            AVT_LONG2:   result := 'l2';
            AVT_LONG4:   result := 'l4';
            AVT_LONG8:   result := 'l8';
            AVT_FLOAT:   result := 'f';
            AVT_FLOAT2:  result := 'f2';
            AVT_FLOAT4:  result := 'f4';
            AVT_FLOAT8:  result := 'f8';
            AVT_DOUBLE:  result := 'd';
            AVT_DOUBLE2: result := 'd2';
            AVT_DOUBLE4: result := 'd4';
            AVT_DOUBLE8: result := 'd8';
            end;
            exit;
        end;
        result := '';
    end;

    class function AVTCodeGenerator.getImplementorsVector(typeRef: AVTTypeStructured): Vector;
    var
        v: Vector;
    begin
        v := Vector.create();
        if typeRef.isClass() or typeRef.isService() and not typeRef.isAbstract() then begin
            if typeRef.isService() then begin
                v.append(ValueOfObject.create(typeRef, false));
            end;
            with typeRef.implements() do while hasMoreElements() do begin
                v.append(ValueOfObject.create(nextElement().objectValue(), false));
            end;
        end;
        result := v;
    end;

    procedure AVTCodeGenerator.compileProgramme(vfs: WriteableVirtualFileSystem; const progFile: UnicodeString; packList: Vector; resList: Hashtable);
    var
        resFold: UnicodeString;
        resPath: AnsiString;
        resDir: AnsiString;
        resName: AnsiString;
        packName: AnsiString;
        packRef: AVTPackage;
        typeRef: AVTType;
        printer: AVTCodePrinter;
        resKey: Value;
    begin
        printer := AVTCodePrinter.create(vfs.createFile(progFile));
        try
            printer.println('include "format.inc"');
            printer.println();
            printer.println('include "macro.inc"');
            printer.println();
            printer.println('include "chapter.code.inc"');
            printer.println();
            printer.printlnHeader('code$begin: ; <fold code>');
            printer.printlnInstruction('call', ['ctx$createMainContext']);
            printer.printlnInstruction('call', ['platform.dependent.Startup$main$']);
            printer.printlnInstruction('call', ['ctx$destroyMainContext']);
            printer.printlnInstruction('include', ['"code/context.inc"']);
            printer.printlnInstruction('include', ['"code/subject.inc"']);
            printer.printlnInstruction('include', ['"code/instruction.inc"']);
            with packList.elements() do while hasMoreElements() do begin
                packRef := nextElement().objectValue() as AVTPackage;
                packName := packRef.fullName;
                if length(packName) <= 0 then begin
                    packName := 'system.package';
                end;
                with packRef.types() do while hasMoreElements() do begin
                    typeRef := nextElement().objectValue() as AVTType;
                    if typeRef is AVTTypeStructured then begin
                        printer.printlnInstruction('include', ['"code/' + packName + '/' + typeNameToSourceName(typeRef) + '"']);
                    end;
                end;
            end;
            printer.printlnHeader('code$end: ; </fold>');
            printer.println();
            printer.println('include "chapter.data.inc"');
            printer.println();
            printer.printlnHeader('data$begin: ; <fold data>');
            with packList.elements() do while hasMoreElements() do begin
                packRef := nextElement().objectValue() as AVTPackage;
                packName := packRef.fullName;
                if length(packName) <= 0 then begin
                    packName := 'system.package';
                end;
                printer.printlnInstruction('include', ['"data/' + packName + '/$package.inc"']);
                with packRef.types() do while hasMoreElements() do begin
                    printer.printlnInstruction('include', ['"data/' + packName + '/' + typeNameToSourceName(nextElement().objectValue() as AVTType) + '"']);
                end;
            end;
            printer.printlnHeader('data$end: ; </fold>');
            printer.println();
            printer.println('include "chapter.debuginfo.inc"');
            printer.println();
            printer.printlnHeader('debuginfo$begin: ; <fold debug info>');
            printer.printlnInstruction('dbinfob', ['"AVTC ' + AVTC_VERSION + ' [' + stringReplace({$I %DATE%}, '/', '-') + ']"']);
            printer.printlnInstruction('include', ['"dbinf/strings.inc"']);
            printer.printlnInstruction('include', ['"dbinf/sources.inc"']);
            printer.printlnInstruction('include', ['"dbinf/entries.inc"']);
            printer.printlnHeader('debuginfo$end: ; </fold>');
            printer.println();
            printer.println('include "chapter.resource.inc"');
            printer.println();
            printer.printlnHeader('resources$begin: ; <fold resources>');
            with resList.keys() do while hasMoreElements() do begin
                resKey := nextElement();
                resFold := resKey.unicodeStringValue();
                resPath := resourceDirToPath(resFold);
                resDir := stringToUTF8(resourceDirToDir(resFold));
                if length(resFold) > 0 then begin
                    resDir := resDir + '/';
                end;
                with (resList.get(resKey).objectValue() as Vector).elements() do while hasMoreElements() do begin
                    resName := nextElement().ansiStringValue();
                    printer.printlnInstruction('avtresource', ['"' + resPath + resName + '"', '"rsrc/' + resDir + resName + '"']);
                end;
            end;
            printer.printlnInstruction('endresource');
            printer.printlnHeader('resources$end: ; </fold>');
            printer.println();
            printer.println('include "chapter.const.inc"');
            printer.println();
            printer.printlnHeader('; <fold constants>');
            printer.printlnInstruction('include', ['"const/instruction.inc"']);
            printer.printlnInstruction('include', ['"const/programme.inc"']);
            printer.printlnInstruction('include', ['"const/context.inc"']);
            printer.printlnHeader('; </fold>');
            printer.println();
            printer.println('include "chapter.end.inc"');
        finally
            printer.close();
        end;
    end;

    procedure AVTCodeGenerator.compileProgrammeConstants(vfs: WriteableVirtualFileSystem; const constFile: UnicodeString);
    var
        i: int;
        j: int;
        count: int;
        value: AVTValue;
        printer: AVTCodePrinter;
    begin
        printer := AVTCodePrinter.create(vfs.createFile(constFile));
        try
            printer.println('; <fold programme''s constants>');
            with value do begin
                printer.printlnHeader('; <fold int2>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('i2');
                with i2 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asInt2 := itemAt(i);
                        printer.printlnInstruction('dd', [avtIntToString(asInt8[0]), avtIntToString(asInt8[1])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold int4>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('i4');
                with i4 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asInt4 := itemAt(i);
                        printer.printlnInstruction('dd', [
                            avtIntToString(asInt8[0]), avtIntToString(asInt8[1]), avtIntToString(asInt8[2]), avtIntToString(asInt8[3])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold int8>');
                printer.printlnInstruction('dalign', [avtByteToString($20)]);
                printer.printLabel('i8');
                with i8 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asInt8 := itemAt(i);
                        printer.printlnInstruction('dd', [
                            avtIntToString(asInt8[0]), avtIntToString(asInt8[1]), avtIntToString(asInt8[2]), avtIntToString(asInt8[3]),
                            avtIntToString(asInt8[4]), avtIntToString(asInt8[5]), avtIntToString(asInt8[6]), avtIntToString(asInt8[7])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold long>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('l');
                with l do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asLong := itemAt(i);
                        printer.printlnInstruction('dq', [avtLongToString(asLong8[0])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold long2>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('l2');
                with l2 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asLong2 := itemAt(i);
                        printer.printlnInstruction('dq', [avtLongToString(asLong8[0]), avtLongToString(asLong8[1])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold long4>');
                printer.printlnInstruction('dalign', [avtByteToString($20)]);
                printer.printLabel('l4');
                with l4 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asLong4 := itemAt(i);
                        printer.printlnInstruction('dq', [
                            avtLongToString(asLong8[0]), avtLongToString(asLong8[1]), avtLongToString(asLong8[2]), avtLongToString(asLong8[3])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold long8>');
                printer.printlnInstruction('dalign', [avtByteToString($40)]);
                printer.printLabel('l8');
                with l8 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asLong8 := itemAt(i);
                        printer.printlnInstruction('dq', [
                            avtLongToString(asLong8[0]), avtLongToString(asLong8[1]), avtLongToString(asLong8[2]), avtLongToString(asLong8[3]),
                            avtLongToString(asLong8[4]), avtLongToString(asLong8[5]), avtLongToString(asLong8[6]), avtLongToString(asLong8[7])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold float>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('f');
                with f do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asFloat := itemAt(i);
                        printer.printlnInstruction('dd', [avtIntToString(asInt8[0])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold float2>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('f2');
                with f2 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asFloat2 := itemAt(i);
                        printer.printlnInstruction('dd', [avtIntToString(asInt8[0]), avtIntToString(asInt8[1])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold float4>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('f4');
                with f4 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asFloat4 := itemAt(i);
                        printer.printlnInstruction('dd', [
                            avtIntToString(asInt8[0]), avtIntToString(asInt8[1]), avtIntToString(asInt8[2]), avtIntToString(asInt8[3])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold float8>');
                printer.printlnInstruction('dalign', [avtByteToString($20)]);
                printer.printLabel('f8');
                with f8 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asFloat8 := itemAt(i);
                        printer.printlnInstruction('dd', [
                            avtIntToString(asInt8[0]), avtIntToString(asInt8[1]), avtIntToString(asInt8[2]), avtIntToString(asInt8[3]),
                            avtIntToString(asInt8[4]), avtIntToString(asInt8[5]), avtIntToString(asInt8[6]), avtIntToString(asInt8[7])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold double>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('d');
                with d do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asDouble := itemAt(i);
                        printer.printlnInstruction('dq', [avtLongToString(asLong8[0])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold double2>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('d2');
                with d2 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asDouble2 := itemAt(i);
                        printer.printlnInstruction('dq', [avtLongToString(asLong8[0]), avtLongToString(asLong8[1])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold double4>');
                printer.printlnInstruction('dalign', [avtByteToString($20)]);
                printer.printLabel('d4');
                with d4 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asDouble4 := itemAt(i);
                        printer.printlnInstruction('dq', [
                            avtLongToString(asLong8[0]), avtLongToString(asLong8[1]), avtLongToString(asLong8[2]), avtLongToString(asLong8[3])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold double8>');
                printer.printlnInstruction('dalign', [avtByteToString($40)]);
                printer.printLabel('d8');
                with d8 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asDouble8 := itemAt(i);
                        printer.printlnInstruction('dq', [
                            avtLongToString(asLong8[0]), avtLongToString(asLong8[1]), avtLongToString(asLong8[2]), avtLongToString(asLong8[3]),
                            avtLongToString(asLong8[4]), avtLongToString(asLong8[5]), avtLongToString(asLong8[6]), avtLongToString(asLong8[7])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold byte2>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('b2');
                with b2 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asByte2 := itemAt(i);
                        printer.printlnInstruction('db', [avtByteToString(asByte8[0]), avtByteToString(asByte8[1])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold byte4>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('b4');
                with b4 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asByte4 := itemAt(i);
                        printer.printlnInstruction('db', [
                            avtByteToString(asByte8[0]), avtByteToString(asByte8[1]), avtByteToString(asByte8[2]), avtByteToString(asByte8[3])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold byte8>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('b8');
                with b8 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asByte8 := itemAt(i);
                        printer.printlnInstruction('db', [
                            avtByteToString(asByte8[0]), avtByteToString(asByte8[1]), avtByteToString(asByte8[2]), avtByteToString(asByte8[3]),
                            avtByteToString(asByte8[4]), avtByteToString(asByte8[5]), avtByteToString(asByte8[6]), avtByteToString(asByte8[7])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold short2>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('s2');
                with s2 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asShort2 := itemAt(i);
                        printer.printlnInstruction('dw', [avtShortToString(asShort8[0]), avtShortToString(asShort8[1])]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold short4>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('s4');
                with s4 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asShort4 := itemAt(i);
                        printer.printlnInstruction('dw', [
                            avtShortToString(asShort8[0]), avtShortToString(asShort8[1]), avtShortToString(asShort8[2]), avtShortToString(asShort8[3])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold short8>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('s8');
                with s8 do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asShort8 := itemAt(i);
                        printer.printlnInstruction('dw', [
                            avtShortToString(asShort8[0]), avtShortToString(asShort8[1]), avtShortToString(asShort8[2]), avtShortToString(asShort8[3]),
                            avtShortToString(asShort8[4]), avtShortToString(asShort8[5]), avtShortToString(asShort8[6]), avtShortToString(asShort8[7])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold real>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('x');
                with x do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asReal := itemAt(i);
                        printer.printlnInstruction('dw', [
                            avtShortToString(asShort8[0]), avtShortToString(asShort8[1]), avtShortToString(asShort8[2]), avtShortToString(asShort8[3]),
                            avtShortToString(asShort8[4])
                        ]);
                    end;
                end;
                if count <= 0 then printer.println();
                printer.printlnHeader('; </fold>');
                printer.println();
                printer.printlnHeader('; <fold string descriptor>');
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('string');
                with strings do begin
                    count := length;
                    for i := 0 to count - 1 do begin
                        asString := itemAt(i);
                        printer.printlnInstruction('dd', [avtIntToString(system.length(asString)), 'st' + avtIntToString(i) + '-($-$04)']);
                    end;
                    if count <= 0 then printer.println();
                    printer.printlnHeader('; </fold>');
                    printer.println();
                    printer.printlnHeader('; <fold string content>');
                    printer.printlnInstruction('dalign', [avtByteToString($10)]);
                    for i := 0 to count - 1 do begin
                        asString := itemAt(i);
                        printer.printLabel('st' + avtIntToString(i));
                        for j := 0 to system.length(asString) - 1 do begin
                            if (j and $0f) = 0 then begin
                                printer.println();
                                printer.printInstruction('dw', [avtShortToString(int(asString[j + 1]))]);
                            end else begin
                                printer.print(', ' + avtShortToString(int(asString[j + 1])));
                            end;
                        end;
                        printer.println();
                    end;
                end;
                printer.printlnHeader('; </fold>');
            end;
            printer.println('; </fold>');
        finally
            printer.close();
        end;
    end;

    procedure AVTCodeGenerator.compileProgrammeDebugInfo(vfs: WriteableVirtualFileSystem; const dbinfDir: UnicodeString);
    var
        i: int;
        j: int;
        len: int;
        inst: AnsiString;
        typn: AnsiString;
        meth: AnsiString;
        srcn: AnsiString;
        astr: AnsiString;
        ustr: UnicodeString;
        printer: AVTCodePrinter;
        strings: AVTStringStorage;
        typeRef: AVTType;
        memberRef: TObject;
        methodRef: AVTMethod;
        source: AVTSource;
    begin
        printer := AVTCodePrinter.create(vfs.createFile(dbinfDir + 'strings.inc'));
        try
            strings := debugInfoStrings;
            printer.println('; <fold programme''s debug info>');
            printer.printlnHeader('; <fold strings>');
            printer.printLabel('debuginfo$string');
            printer.println();
            for i := 0 to strings.length - 1 do begin
                astr := stringToUTF8(strings[i]);
                if not stringStartsWith('<', astr) or not stringEndsWith('>', astr) then begin
                    printer.printlnInstruction('dstr', [astr]);
                end else begin
                    printer.printlnInstruction('dstrs', [stringCopy(astr, 2, length(astr))]);
                end;
            end;
            printer.printLabel('.end');
            printer.printlnHeader('; </fold>');
            printer.println('; </fold>');
        finally
            printer.close();
        end;
        printer := AVTCodePrinter.create(vfs.createFile(dbinfDir + 'sources.inc'));
        try
            strings := debugInfoSources;
            printer.println('; <fold programme''s debug info>');
            printer.printlnHeader('; <fold sources>');
            printer.printlnInstruction('dalign', [avtByteToString($02)]);
            printer.printLabel('debuginfo$source');
            printer.println();
            for i := 0 to strings.length - 1 do begin
                astr := 'avt' + avtIntToString(i);
                ustr := strings[i];
                len := length(ustr);
                printer.printInstruction('dsrc', [astr]);
                for j := 0 to len - 1 do begin
                    if (j and 7) > 0 then begin
                        printer.print(', ');
                    end else begin
                        printer.println(', \');
                        printer.printContinuation('');
                    end;
                    printer.print(avtShortToString(int(ustr[j + 1])));
                end;
                printer.println();
            end;
            printer.printLabel('.end');
            printer.printlnHeader('; </fold>');
            printer.println('; </fold>');
        finally
            printer.close();
        end;
        printer := AVTCodePrinter.create(vfs.createFile(dbinfDir + 'entries.inc'));
        try
            printer.println('; <fold programme''s debug info>');
            printer.printlnHeader('; <fold code regions>');
            printer.printlnInstruction('dalign', [avtByteToString($20)]);
            printer.printLabel('debuginfo$entry');
            printer.println();
            with programme.packages() do while hasMoreElements() do with (nextElement().objectValue() as AVTPackage).types() do while hasMoreElements() do begin
                typeRef := nextElement().objectValue() as AVTType;
                typn := typeRef.fasmFullName;
                source := typeRef.source;
                if typeRef is AVTTypeStructured then with AVTTypeStructured(typeRef).members() do while hasMoreElements() do begin
                    memberRef := nextElement().objectValue();
                    if not(memberRef is AVTMethod) then continue;
                    methodRef := AVTMethod(memberRef);
                    if methodRef.isAbstract() then continue;
                    astr := methodRef.simpleName;
                    meth := methodRef.fasmFullName;
                    if source = nil then begin
                        ustr := '';
                    end else begin
                        ustr := source.simpleName;
                    end;
                    if not stringStartsWith('<', astr) or not stringEndsWith('>', astr) then begin
                        inst := 'dent';
                    end else begin
                        inst := 'dents';
                        astr := stringCopy(astr, 2, length(astr));
                    end;
                    srcn := avtIntToString(strings.indexOf(ustr));
                    len := methodRef.getDebugInfoLength();
                    if len <= 0 then begin
                        printer.printlnInstruction(inst, [meth, meth + '.D.END', 'avt' + srcn, '0', typn, astr]);
                        continue;
                    end;
                    j := 1;
                    for i := 0 to len - 1 do begin
                        if j < len then begin
                            printer.printlnInstruction(inst, [meth + labelNumberToString('D', i), meth + labelNumberToString('D', j), 'avt' + srcn, intToString(methodRef.getDebugInfoLineNumber(i)), typn, astr]);
                        end else begin
                            printer.printlnInstruction(inst, [meth + labelNumberToString('D', i), meth + '.D.END', 'avt' + srcn, intToString(methodRef.getDebugInfoLineNumber(i)), typn, astr]);
                        end;
                        inc(j);
                    end;
                end;
            end;
            printer.printLabel('.end');
            printer.printlnHeader('; </fold>');
            printer.println('; </fold>');
        finally
            printer.close();
        end;
    end;

    procedure AVTCodeGenerator.compilePackage(currPackRef, nextPackRef: AVTPackage; vfs: WriteableVirtualFileSystem; const codeDir, dataDir, nativesDir: UnicodeString);
    var
        typ: AVTType;
        fileName: UnicodeString;
    begin
        with currPackRef.types() do while hasMoreElements() do begin
            typ := nextElement().objectValue() as AVTType;
            fileName := stringToUTF16(typeNameToSourceName(typ));
            if typ is AVTTypeStructured then begin
                compileClassCode(AVTTypeStructured(typ), vfs, codeDir + fileName, nativesDir + fileName);
                compileClassData(AVTTypeStructured(typ), vfs, dataDir + fileName);
            end else
            if typ is AVTTypePrimitive then begin
                compilePrimitiveData(AVTTypePrimitive(typ), vfs, dataDir + fileName);
            end;
        end;
        compilePackageData(currPackRef, nextPackRef, vfs, dataDir + '$package.inc');
    end;

    procedure AVTCodeGenerator.compilePackageData(currPackRef, nextPackRef: AVTPackage; vfs: WriteableVirtualFileSystem; const dataFile: UnicodeString);
    var
        fullName: AnsiString;
        packName: AnsiString;
        types: Vector;
        printer: AVTCodePrinter;
    begin
        fullName := currPackRef.fullName;
        if length(fullName) <= 0 then begin
            packName := 'system.package';
        end else begin
            packName := fullName;
        end;
        types := nil;
        printer := AVTCodePrinter.create(vfs.createFile(dataFile));
        try
            types := Vector.create();
            with currPackRef.types() do while hasMoreElements() do begin
                types.append(ValueOfObject.create(nextElement().objectValue(), false));
            end;
            printer.println('; package ' + packName + ' (data)');
            printer.println();
            printer.printlnHeader('package ' + packName);
            printer.printlnInstruction('dd', [avtIntToString(FLAG_PUBLIC) + ' ; modifiers']);
            printer.printlnInstruction('dd', [avtIntToString(strings.indexAcquire(fullName)) + ' ; full name index']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(types.size()) + ' ; types quantity']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', ['-.package+.this', avtIntToString(0), avtIntToString(0), avtIntToString(0)]);
            if nextPackRef <> nil then begin
                printer.printlnInstruction('dq', ['-.package+' + nextPackRef.fullName + ' ; next package']);
            end else begin
                printer.printlnInstruction('dq', [avtLongToString(0) + ' ; next package']);
            end;
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printLabel('.this');
            with types.elements() do while hasMoreElements() do begin
                printer.printlnInstruction('dq', ['-.this+' + (nextElement().objectValue() as AVTType).fasmFullName]);
            end;
        finally
            printer.close();
            types.free();
        end;
    end;

    procedure AVTCodeGenerator.compilePrimitiveData(typeRef: AVTTypePrimitive; vfs: WriteableVirtualFileSystem; const dataFile: UnicodeString);
    var
        printer: AVTCodePrinter;
    begin
        printer := AVTCodePrinter.create(vfs.createFile(dataFile));
        try
            printer.println('; ' + typeRef.fullName + ' (data)');
            printer.println();
            printer.printlnHeader('class ' + typeRef.fasmFullName);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.flags) + ' ; modifiers']);
            printer.printlnInstruction('dd', [avtIntToString(strings.indexAcquire(typeRef.fullName)) + ' ; simple name index']);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.fieldWidth) + ' ; instance size']);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.primitiveKind) + ' ; structure size']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reference fields quantity']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; virtual methods quantity']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; implementors quantity']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; services quantity']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; struct reference fields quantity ']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0), avtIntToString(0), avtIntToString(0), avtIntToString(0)]);
            printer.printlnInstruction('dd', [avtIntToString(0), avtIntToString(0), avtIntToString(0), avtIntToString(0)]);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; super type']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; component type']);
            printer.printlnInstruction('dq', ['-.class+system.package ; owned package']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; main constructor']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; source simple name offset']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
        finally
            printer.close();
        end;
    end;

    procedure AVTCodeGenerator.compileClassCode(typeRef: AVTTypeStructured; vfs: WriteableVirtualFileSystem; const codeFile, nativesFile: UnicodeString);
    var
        natives: Input;
        printer: AVTCodePrinter;
        memberRef: TObject;
        methodRef: AVTMethod;
    begin
        printer := AVTCodePrinter.create(vfs.createFile(codeFile));
        try
            { код методов на fasm }
            with typeRef.members() do while hasMoreElements() do begin
                memberRef := nextElement().objectValue();
                if (memberRef is AVTMethod) and ((AVTMethod(memberRef).flags and FLAG_NATIVE) <> 0) then begin
                    natives := vfs.openFileForReading(nativesFile);
                    try
                        copyData(natives, printer);
                        printer.println();
                    finally
                        natives.close();
                    end;
                    break;
                end;
            end;
            { код методов на ПВТ }
            printer.println('; <fold ' + typeRef.fullName + ' (code)>');
            with typeRef.members() do while hasMoreElements() do begin
                memberRef := nextElement().objectValue();
                if not(memberRef is AVTMethod) then continue;
                methodRef := AVTMethod(memberRef);
                if (methodRef.code = nil) and not methodRef.isAbstract() then begin
                    debugInfoStrings.indexAcquire(methodRef.simpleName);
                end;
                if (methodRef.flags and FLAG_NATIVE) = 0 then begin
                    compileMethod(methodRef, printer);
                end;
            end;
            printer.println('; </fold>');
            printer.println();
            { реализации сервисов }
            with getImplementorsVector(typeRef) do begin
                try
                    printer.println('; <fold ' + typeRef.fullName + ' (implementors)>');
                    with elements() do while hasMoreElements() do begin
                        compileImplementors(typeRef, nextElement().objectValue() as AVTTypeStructured, printer);
                    end;
                    printer.println('; </fold>');
                finally
                    free();
                end;
            end;
        finally
            printer.close();
        end;
    end;

    procedure AVTCodeGenerator.compileClassData(typeRef: AVTTypeStructured; vfs: WriteableVirtualFileSystem; const dataFile: UnicodeString);
    var
        index: int;
        srcName: UnicodeString;
        packName: AnsiString;
        implName: AnsiString;
        services: Vector;
        implementors: Vector;
        objectReferenceFields: Vector;
        structReferenceFields: Vector;
        mainConstructor: AVTInstInit;
        printer: AVTCodePrinter;
        classRef: AVTTypeStructured;
        memberRef: AVTMember;
        fieldValue: Value;
        startValue: AVTConstant;
        constType: AVTType;
        source: AVTSource;
    begin
        packName := typeRef.package.fullName;
        if length(packName) <= 0 then begin
            packName := 'system.package';
        end;
        implName := '-.impl+' + typeRef.fasmArgumentName + '$$impl$$';
        services := nil;
        implementors := nil;
        objectReferenceFields := nil;
        structReferenceFields := nil;
        printer := AVTCodePrinter.create(vfs.createFile(dataFile));
        try
            services := Vector.create();
            implementors := getImplementorsVector(typeRef);
            objectReferenceFields := Vector.create();
            structReferenceFields := Vector.create();
            mainConstructor := nil;
            with typeRef.implements() do while hasMoreElements() do begin
                services.append(ValueOfObject.create(nextElement().objectValue(), false));
            end;
            classRef := typeRef;
            repeat
                index := 0;
                with classRef.members() do while hasMoreElements() do begin
                    memberRef := nextElement().objectValue() as AVTMember;
                    if (mainConstructor = nil) and ((classRef = typeRef) or (typeRef.isService() and (classRef = typeRef.extends()))) and (memberRef.visibility >= AVT_PUBLIC) and (memberRef is AVTInstInit) and (AVTInstInit(memberRef).getArgumentsCount() = 0) then begin
                        mainConstructor := AVTInstInit(memberRef);
                    end;
                    if not memberRef.isStatic() and (length(memberRef.simpleName) > 0) and (memberRef is AVTField) then with AVTField(memberRef) do if valueType is AVTTypeStructured then begin
                        fieldValue := ValueOfObject.create(memberRef, false);
                        objectReferenceFields.insert(index, fieldValue);
                        if classRef.isStruct() then begin
                            structReferenceFields.insert(index, fieldValue);
                        end;
                        inc(index);
                    end;
                end;
                classRef := classRef.extends();
            until classRef = nil;
            printer.println('; ' + typeRef.fullName + ' (data)');
            printer.println();
            printer.printlnHeader('class ' + typeRef.fasmFullName);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.flags) + ' ; modifiers']);
            printer.printlnInstruction('dd', [avtIntToString(strings.indexAcquire(typeRef.simpleName)) + ' ; simple name index']);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.instanceSize) + ' ; instance size']);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.structureSize) + ' ; structure size']);
            printer.printlnInstruction('dd', [avtIntToString(objectReferenceFields.size()) + ' ; reference fields quantity']);
            printer.printlnInstruction('dd', [avtIntToString(typeRef.virtualMethodsCount) + ' ; virtual methods quantity']);
            printer.printlnInstruction('dd', [avtIntToString(implementors.size()) + ' ; implementors quantity']);
            printer.printlnInstruction('dd', [avtIntToString(services.size()) + ' ; services quantity']);
            printer.printlnInstruction('dd', [avtIntToString(structReferenceFields.size()) + ' ; struct reference fields quantity ']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dd', ['-.class+.refs', '-.class+.virt', '-.class+.impl', '-.class+.serv']);
            if typeRef.isStruct() then begin
                printer.printlnInstruction('dd', ['-.class+.stru', avtIntToString(0), avtIntToString(0), avtIntToString(0)]);
            end else begin
                printer.printlnInstruction('dd', [avtIntToString(0), avtIntToString(0), avtIntToString(0), avtIntToString(0)]);
            end;
            classRef := typeRef.extends();
            if classRef <> nil then begin
                printer.printlnInstruction('dq', ['-.class+' + classRef.fasmFullName + ' ; super type']);
            end else begin
                printer.printlnInstruction('dq', [avtLongToString(0) + ' ; super type']);
            end;
            if typeRef is AVTTypeArray then begin
                printer.printlnInstruction('dq', ['-.class+' + AVTTypeArray(typeRef).elementType.fasmFullName + ' ; component type']);
            end else begin
                printer.printlnInstruction('dq', [avtLongToString(0) + ' ; component type']);
            end;
            printer.printlnInstruction('dq', ['-.class+' + packName + ' ; owned package']);
            if mainConstructor <> nil then begin
                printer.printlnInstruction('dq', ['-.class+' + mainConstructor.fasmFullName + ' ; main constructor']);
            end else begin
                printer.printlnInstruction('dq', [avtLongToString(0) + ' ; main constructor']);
            end;
            source := typeRef.source;
            if source = nil then begin
                srcName := '';
            end else begin
                srcName := source.simpleName;
            end;
            printer.printlnInstruction('dd', ['-.class+debuginfo$source.avt' + avtIntToString(debugInfoSources.indexAcquire(srcName)) + ' ; source simple name offset']);
            printer.printlnInstruction('dd', [avtIntToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printlnInstruction('dq', [avtLongToString(0) + ' ; reserved field']);
            printer.printLabel('.refs');
            with objectReferenceFields.elements() do while hasMoreElements() do with nextElement().objectValue() as AVTField do begin
                printer.printlnInstruction('dd', [fasmFullName]);
            end;
            printer.printlnInstruction('dalign', [avtByteToString($08)]);
            printer.printLabel('.virt');
            with typeRef.virtualMethods() do while hasMoreElements() do begin
                printer.printlnInstruction('dq', ['-.virt+' + (nextElement().objectValue() as AVTMember).fasmFullName]);
            end;
            printer.printlnInstruction('dalign', [avtByteToString($10)]);
            printer.printLabel('.impl');
            with implementors.elements() do while hasMoreElements() do with nextElement().objectValue() as AVTTypeStructured do begin
                printer.printlnInstruction('dq', [implName + fasmArgumentName, '-.impl+' + fasmFullName]);
            end;
            printer.printLabel('.serv');
            with services.elements() do while hasMoreElements() do begin
                printer.printlnInstruction('dq', ['-.serv+' + (nextElement().objectValue() as AVTTypeStructured).fasmFullName]);
            end;
            if typeRef.isStruct() then begin
                printer.printlnInstruction('dalign', [avtByteToString($10)]);
                printer.printLabel('.stru');
                with structReferenceFields.elements() do while hasMoreElements() do with nextElement().objectValue() as AVTField do begin
                    printer.printlnInstruction('dd', [fasmFullName, fasmStructName, '-.stru+' + valueType.fasmFullName, avtIntToString(length)]);
                end;
            end;
            printer.println(LINE_ENDING);
            with typeRef.members() do while hasMoreElements() do begin
                memberRef := nextElement().objectValue() as AVTMember;
                if (memberRef is AVTField) and memberRef.isStatic() then with AVTField(memberRef) do begin
                    startValue := valueConst;
                    if (startValue <> nil) and memberRef.isFinal() then begin
                        constType := startValue.valueType;
                        if constType is AVTTypePrimitive then with startValue.value do begin
                            case AVTTypePrimitive(constType).primitiveKind of
                            AVT_BOOLEAN: printer.printlnHeader(fasmFullName + ' = ' + avtBooleanValueToString(asBoolean));
                            AVT_CHAR:    printer.printlnHeader(fasmFullName + ' = ' + avtIntValueToString(int(asChar)));
                            AVT_REAL:    printer.printlnHeader('label ' + fasmFullName + ' at x+' + intToString(x.indexAcquire(asReal) * 10));
                            AVT_BYTE:    printer.printlnHeader(fasmFullName + ' = ' + avtIntValueToString(asByte));
                            AVT_BYTE2:   printer.printlnHeader('label ' + fasmFullName + ' at b2+' + avtIntToString(b2.indexAcquire(asByte2) * 2));
                            AVT_BYTE4:   printer.printlnHeader('label ' + fasmFullName + ' at b4+' + avtIntToString(b4.indexAcquire(asByte4) * 4));
                            AVT_BYTE8:   printer.printlnHeader('label ' + fasmFullName + ' at b8+' + avtIntToString(b8.indexAcquire(asByte8) * 8));
                            AVT_SHORT:   printer.printlnHeader(fasmFullName + ' = ' + avtIntValueToString(asShort));
                            AVT_SHORT2:  printer.printlnHeader('label ' + fasmFullName + ' at s2+' + avtIntToString(s2.indexAcquire(asShort2) * 4));
                            AVT_SHORT4:  printer.printlnHeader('label ' + fasmFullName + ' at s4+' + avtIntToString(s4.indexAcquire(asShort4) * 8));
                            AVT_SHORT8:  printer.printlnHeader('label ' + fasmFullName + ' at s8+' + avtIntToString(s8.indexAcquire(asShort8) * 16));
                            AVT_INT:     printer.printlnHeader(fasmFullName + ' = ' + avtIntValueToString(asInt));
                            AVT_INT2:    printer.printlnHeader('label ' + fasmFullName + ' at i2+' + avtIntToString(i2.indexAcquire(asInt2) * 8));
                            AVT_INT4:    printer.printlnHeader('label ' + fasmFullName + ' at i4+' + avtIntToString(i4.indexAcquire(asInt4) * 16));
                            AVT_INT8:    printer.printlnHeader('label ' + fasmFullName + ' at i8+' + avtIntToString(i8.indexAcquire(asInt8) * 32));
                            AVT_LONG:    printer.printlnHeader('label ' + fasmFullName + ' at l+' + avtIntToString(l.indexAcquire(asLong) * 8));
                            AVT_LONG2:   printer.printlnHeader('label ' + fasmFullName + ' at l2+' + avtIntToString(l2.indexAcquire(asLong2) * 16));
                            AVT_LONG4:   printer.printlnHeader('label ' + fasmFullName + ' at l4+' + avtIntToString(l4.indexAcquire(asLong4) * 32));
                            AVT_LONG8:   printer.printlnHeader('label ' + fasmFullName + ' at l8+' + avtIntToString(l8.indexAcquire(asLong8) * 64));
                            AVT_FLOAT:   printer.printlnHeader('label ' + fasmFullName + ' at f+' + avtIntToString(f.indexAcquire(asFloat) * 4));
                            AVT_FLOAT2:  printer.printlnHeader('label ' + fasmFullName + ' at f2+' + avtIntToString(f2.indexAcquire(asFloat2) * 8));
                            AVT_FLOAT4:  printer.printlnHeader('label ' + fasmFullName + ' at f4+' + avtIntToString(f4.indexAcquire(asFloat4) * 16));
                            AVT_FLOAT8:  printer.printlnHeader('label ' + fasmFullName + ' at f8+' + avtIntToString(f8.indexAcquire(asFloat8) * 32));
                            AVT_DOUBLE:  printer.printlnHeader('label ' + fasmFullName + ' at d+' + avtIntToString(d.indexAcquire(asDouble) * 8));
                            AVT_DOUBLE2: printer.printlnHeader('label ' + fasmFullName + ' at d2+' + avtIntToString(d2.indexAcquire(asDouble2) * 16));
                            AVT_DOUBLE4: printer.printlnHeader('label ' + fasmFullName + ' at d4+' + avtIntToString(d4.indexAcquire(asDouble4) * 32));
                            AVT_DOUBLE8: printer.printlnHeader('label ' + fasmFullName + ' at d8+' + avtIntToString(d8.indexAcquire(asDouble8) * 64));
                            end;
                        end;
                    end else begin
                        printer.printlnHeader('glob ' + fasmFullName);
                        if startValue = nil then begin
                            case valueType.fieldWidth of
                            $01..$10:
                                printer.printlnInstruction('dq', ['0', '0']);
                            $11..$20:
                                printer.printlnInstruction('dq', ['0', '0', '0', '0']);
                            $21..$40:
                                printer.printlnInstruction('dq', ['0', '0', '0', '0', '0', '0', '0', '0']);
                            end;
                        end else with startValue.value do begin
                            case valueType.fieldWidth of
                            $01..$10: begin
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[0]), avtLongToString(asLong8[1])]);
                            end;
                            $11..$20: begin
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[0]), avtLongToString(asLong8[1])]);
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[2]), avtLongToString(asLong8[3])]);
                            end;
                            $21..$40: begin
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[0]), avtLongToString(asLong8[1])]);
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[2]), avtLongToString(asLong8[3])]);
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[4]), avtLongToString(asLong8[5])]);
                                printer.printlnInstruction('dq', [avtLongToString(asLong8[6]), avtLongToString(asLong8[7])]);
                            end;
                            end;
                        end;
                    end;
                end;
            end;
            with typeRef.members() do while hasMoreElements() do begin
                memberRef := nextElement().objectValue() as AVTMember;
                if not memberRef.isStatic() and (length(memberRef.simpleName) > 0) and (memberRef is AVTField) then with AVTField(memberRef) do if not typeRef.isStruct() or (valueType is AVTTypeStructured) then begin
                    printer.printlnHeader('field ' + avtIntToString(instanceOffset) + ', ' + fasmFullName);
                end;
            end;
            if typeRef.isStruct() then begin
                printer.println();
                with typeRef.members() do while hasMoreElements() do begin
                    memberRef := nextElement().objectValue() as AVTMember;
                    if not memberRef.isStatic() and (length(memberRef.simpleName) > 0) and (memberRef is AVTField) then with AVTField(memberRef) do begin
                        printer.printlnHeader('field ' + avtIntToString(structureOffset) + ', ' + fasmStructName);
                    end;
                end;
            end;
        finally
            printer.close();
            services.free();
            implementors.free();
            objectReferenceFields.free();
            structReferenceFields.free();
        end;
    end;

    procedure AVTCodeGenerator.compileMethod(methodRef: AVTMethod; printer: AVTCodePrinter);
    var
        isVoid: boolean;
        ehEmpty: boolean;
        i: int;
        j: int;
        lim: int;
        lastLine: int;
        currLine: int;
        locsList: Vector;
        locsTable: Hashtable;
        local: AVTVariable;
        element: Value;
        key: Value;
        code: AVTCode;
        node: AVTNode;
        returnTypeRef: AVTType;
        source: AVTSource;
    begin
        locsTable := nil;
        locsList := Vector.create();
        try
            { заголовок }
            if not methodRef.isStatic() then begin
                locsList.append(ValueOfObject.create(methodRef.argumentThis, false));
            end;
            with methodRef.arguments() do while hasMoreElements() do begin
                locsList.append(ValueOfObject.create(nextElement().objectValue(), false));
            end;
            printer.println();
            lim := locsList.size() - 1;
            if lim >= 0 then begin
                printer.printlnHeader('method ' + methodRef.fasmFullName + ', \');
            end else begin
                printer.printHeader('method ' + methodRef.fasmFullName + '  ;');
            end;
            for i := 0 to lim do begin
                local := locsList.elementAt(i).objectValue() as AVTVariable;
                if i < lim then begin
                    printer.printlnInstruction(local.fasmShortName + ', ' + local.valueType.fasmArgumentName + ', \');
                end else begin
                    printer.printInstruction(local.fasmShortName + ', ' + local.valueType.fasmArgumentName + '  ;');
                end;
            end;
            returnTypeRef := methodRef.returnType;
            isVoid := returnTypeRef.isVoid();
            if not isVoid then begin
                printer.print(' returns ' + returnTypeRef.fullName);
            end;
            with methodRef.throws() do if hasMoreElements() then begin
                if isVoid then begin
                    printer.print(' throws ');
                end else begin
                    printer.print('; throws ');
                end;
                repeat
                    printer.print((nextElement().objectValue() as AVTType).fullName);
                    if not hasMoreElements() then break;
                    printer.print(', ');
                until false;
            end;
            code := methodRef.code;
            if code = nil then begin
                printer.println();
                exit;
            end;
            printer.println(' <fold >');
            { локальные переменные }
            locsList.clear();
            locsTable := Hashtable.create();
            with code.instructions() do while hasMoreElements() do with nextElement().objectValue() as AVTNode do begin
                case instruction of
                DECLARE_VARIABLE, AVT_CATCH: begin
                    local := operand1 as AVTVariable;
                    key := ValueOfAnsiString.create(local.simpleName);
                    element := locsTable.get(key);
                    if element = nil then begin
                        locsList.append(key);
                        locsTable.put(key, ValueOfObject.create(local, false));
                    end else
                    if local.valueType.fieldWidth > (element.objectValue() as AVTVariable).valueType.fieldWidth then begin
                        locsTable.put(key, ValueOfObject.create(local, false));
                    end;
                end;
                end;
            end;
            with locsList.elements() do while hasMoreElements() do with locsTable.get(nextElement()).objectValue() as AVTVariable do begin
                printer.printlnInstruction('loc', [fasmShortName, valueType.fasmArgumentName]);
            end;
        finally
            locsTable.free();
            locsList.free();
        end;
        { код метода }
        isVoid := true;
        lastLine := -1;
        source := methodRef.source;
        if source = nil then begin
            debugInfoSources.indexAcquire('');
        end else begin
            debugInfoSources.indexAcquire(source.simpleName);
        end;
        debugInfoStrings.indexAcquire(methodRef.simpleName);
        with code.instructions() do while hasMoreElements() do if (nextElement().objectValue() as AVTNode).lineIndex >= 0 then begin
            isVoid := false;
            break;
        end;
        if isVoid then begin
            printer.printLabel(labelNumberToString('D', methodRef.addDebugInfoLineNumber(0)));
        end;
        with code.instructions() do while hasMoreElements() do begin
            node := nextElement().objectValue() as AVTNode;
            currLine := node.lineIndex;
            if (currLine <> -1) and (currLine <> lastLine) then begin
                printer.printlnSourceLine(stringToUTF8(source.fileName) + '[' + intToString(currLine + 1) + ']: ' + stringToUTF8(stringTrim(source[currLine])));
                printer.printLabel(labelNumberToString('D', methodRef.addDebugInfoLineNumber(currLine + 1)));
                lastLine := currLine;
            end;
            compileInstructionNode(node, methodRef, printer);
        end;
        { код обработчика исключений }
        ehEmpty := true;
        for i := code.length - 1 downto 0 do with code[i] do if instruction = AVT_TRY_BEGIN then begin
            if ehEmpty then begin
                printer.printlnInstruction('eenter');
            end else begin
                printer.printLabel('@@');
            end;
            ehEmpty := false;
            printer.printlnInstruction('tryblock', [labelNumberToString(labelNumber), labelNumberToString(child[1].labelNumber), '@F']);
            for j := 3 to length - 1 do with child[j] do begin
                case instruction of
                AVT_CATCH:   printer.printlnInstruction('catchblock', [(operand1 as AVTVariable).valueType.fasmFullName, labelNumberToString(labelNumber)]);
                AVT_FINALLY: printer.printlnInstruction('finallyblock', [labelNumberToString(labelNumber)]);
                end;
            end;
        end;
        if not ehEmpty then begin
            printer.printLabel('@@');
        end;
        printer.printlnInstruction('eleave');
        { закрытие свёртывания }
        printer.printlnHeader('; </fold>');
    end;

    procedure AVTCodeGenerator.compileInstructionNode(node: AVTNode; methodRef: AVTMethod; printer: AVTCodePrinter);
    var
        a: int;
        b: int;
        i: int;
        kind: int;
        vindex: int;
        sindex: int;
        nextOrder: int;
        labelNumber: int;
        caseCurVal: int;
        caseMinVal: int;
        caseMaxVal: int;
        caseCountLookup: int;
        caseCountTable: long;
        caseValues: Vector;
        caseEnum: Enumeration;
        jumpCase: AVTNode;
        jumpDefault: AVTNode;
        jumpIsTrue: AVTNode;
        jumpIsFalse: AVTNode;
        typ: AVTType;
        prim: AVTTypePrimitive;
        typeOldRef: AVTType;
        typeNewRef: AVTType;
        primOldRef: AVTTypePrimitive;
        primImmRef: AVTTypePrimitive;
        primNewRef: AVTTypePrimitive;
        interactMember: _Object;
    begin
        labelNumber := node.labelNumber;
        if labelNumber >= 0 then begin
            printer.printLabel(labelNumberToString(labelNumber));
        end;
        jumpIsFalse := node.jumpIsFalse;
        jumpIsTrue := node.jumpIsTrue;
        nextOrder := node.order + 1;
        case node.instruction of
        AVT_INSTANCEOF: begin
            printer.printlnInstruction('rinstanceof', [(node.operand1 as AVTType).fasmFullName]);
        end;
        AVT_SWITCH: begin
            caseEnum := node.jumpCaseValues();
            if not caseEnum.hasMoreElements() then begin
                printer.printlnInstruction('pop1');
                exit;
            end;
            caseValues := Vector.create();
            try
                repeat
                    caseValues.append(caseEnum.nextElement());
                until not caseEnum.hasMoreElements();
                caseCountLookup := caseValues.size();
                caseMaxVal := caseValues.firstElement().intValue();
                caseMinVal := caseMaxVal;
                for i := 1 to caseCountLookup - 1 do begin
                    caseCurVal := caseValues.elementAt(i).intValue();
                    if caseMinVal > caseCurVal then caseMinVal := caseCurVal;
                    if caseMaxVal < caseCurVal then caseMaxVal := caseCurVal;
                end;
                jumpDefault := node.jumpDefault;
                caseCountTable := long(caseMaxVal) - long(caseMinVal) + 1;
                if (caseCountTable shl 2) + 48 <= (long(caseCountLookup) + 1) * 10 then begin
                    { itableswitch }
                    printer.printlnInstruction('itableswitch', [labelNumberToString(jumpDefault.labelNumber), avtIntToString(caseMinVal), '\']);
                    for i := 0 to int(caseCountTable) - 1 do begin
                        jumpCase := node.jumpCaseGetNode(caseMinVal + i);
                        if jumpCase = nil then jumpCase := jumpDefault;
                        if i = 0 then begin
                            printer.printContinuation(labelNumberToString(jumpCase.labelNumber));
                        end else
                        if (i and 7) > 0 then begin
                            printer.print(', ' + labelNumberToString(jumpCase.labelNumber));
                        end else begin
                            printer.println(', \');
                            printer.printContinuation(labelNumberToString(jumpCase.labelNumber));
                        end;
                    end;
                end else begin
                    { ilookupswitch }
                    dec(caseCountLookup);
                    printer.printlnInstruction('ilookupswitch', [labelNumberToString(jumpDefault.labelNumber), '\']);
                    for i := 0 to caseCountLookup do begin
                        caseCurVal := caseValues.elementAt(i).intValue();
                        printer.printContinuation(avtIntToString(caseCurVal) + ', ' + labelNumberToString(node.jumpCaseGetNode(caseCurVal).labelNumber));
                        if i < caseCountLookup then printer.println(', \');
                    end;
                end;
                printer.println();
            finally
                caseValues.free();
            end;
        end;
        AVT_CASE, AVT_DEFAULT, AVT_TRY_BEGIN, AVT_TRY_END: begin
            { нет инструкций, только метка }
        end;
        AVT_RETURN, JUMP: begin
            printer.printlnInstruction('jmp', [labelNumberToString(node.jumpDefault.labelNumber)]);
        end;
        AVT_THROW: begin
            printer.printlnInstruction('rthrow');
        end;
        AVT_CATCH, DECLARE_VARIABLE, STORE_VARIABLE: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(valueType)) + 'storeloc', [fasmShortName]);
            end;
        end;
        AVT_FINALLY: begin
            printer.printlnInstruction('fenter');
        end;
        AVT_FINALLY_RETURN: begin
            printer.printlnInstruction('fleave');
        end;
        AVT_FINALLY_INVOKE: begin
            printer.printlnInstruction('invokefinally', [labelNumberToString((node.operand1 as AVTNode).labelNumber)]);
        end;
        AVT_CLINIT_TRY_LOCK: begin
            printer.printlnInstruction('statictrylock', [(node.operand1 as AVTTypeStructured).fasmFullName]);
        end;
        AVT_CLINIT_UNLOCK: begin
            printer.printlnInstruction('staticunlock');
        end;
        AVT_MONITOR_ENTER: begin
            printer.printlnInstruction('rmonitorenter');
        end;
        AVT_MONITOR_LEAVE: begin
            printer.printlnInstruction('rmonitorleave');
        end;
        AVT_ENTER: begin
            if (methodRef.flags and FLAG_INTERRUPT) <> 0 then begin
                printer.printlnInstruction('ienter');
            end else begin
                printer.printlnInstruction('menter');
            end;
        end;
        AVT_LEAVE: begin
            if (methodRef.flags and FLAG_INTERRUPT) <> 0 then begin
                printer.printlnInstruction('ileave');
            end else begin
                printer.printlnInstruction('mleave' + typeReturnSuffix(node.operand1 as AVTType));
            end;
        end;
        LOAD_VARIABLE: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(valueType)) + 'loadloc', [fasmShortName]);
            end;
        end;
        LOAD_STATIC: begin
            with node.operand1 as AVTField do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'load', ['[' + fasmFullName + ']']);
            end;
        end;
        LOAD_CONST: begin
            with node.operand1 as AVTConstant, value do begin
                typ := valueType;
                if (typ = nil) or (typ is AVTTypeStructured) then begin
                    typ := asClass;
                    if isNull then begin
                        printer.printlnInstruction('rloadnull');
                    end else
                    if typ <> nil then begin
                        printer.printlnInstruction('rloadclass', [typ.fasmFullName]);
                    end else begin
                        printer.printlnInstruction('rloadstring', [avtIntToString(strings.indexAcquire(asString))]);
                    end;
                end else
                if typ is AVTTypePrimitive then begin
                    case AVTTypePrimitive(typ).primitiveKind of
                    AVT_BOOLEAN: begin
                        if asBoolean then begin
                            printer.printlnInstruction('bloadt');
                        end else begin
                            printer.printlnInstruction('bloadf');
                        end;
                    end;
                    AVT_CHAR:    printer.printlnInstruction('cload', [avtShortToString(int(asChar))]);
                    AVT_REAL:    printer.printlnInstruction('xload', ['[x+' + intToString(x.indexAcquire(asReal) * 10) + ']']);
                    AVT_BYTE:    printer.printlnInstruction('bload', [avtByteToString(asByte)]);
                    AVT_BYTE2:   printer.printlnInstruction('b2load', ['[b2+' + avtIntToString(b2.indexAcquire(asByte2) * 2) + ']']);
                    AVT_BYTE4:   printer.printlnInstruction('b4load', ['[b4+' + avtIntToString(b4.indexAcquire(asByte4) * 4) + ']']);
                    AVT_BYTE8:   printer.printlnInstruction('b8load', ['[b8+' + avtIntToString(b8.indexAcquire(asByte8) * 8) + ']']);
                    AVT_SHORT:   printer.printlnInstruction('sload', [avtShortToString(asShort)]);
                    AVT_SHORT2:  printer.printlnInstruction('s2load', ['[s2+' + avtIntToString(s2.indexAcquire(asShort2) * 4) + ']']);
                    AVT_SHORT4:  printer.printlnInstruction('s4load', ['[s4+' + avtIntToString(s4.indexAcquire(asShort4) * 8) + ']']);
                    AVT_SHORT8:  printer.printlnInstruction('s8load', ['[s8+' + avtIntToString(s8.indexAcquire(asShort8) * 16) + ']']);
                    AVT_INT:     printer.printlnInstruction('iload', [avtIntToString(asInt)]);
                    AVT_INT2:    printer.printlnInstruction('i2load', ['[i2+' + avtIntToString(i2.indexAcquire(asInt2) * 8) + ']']);
                    AVT_INT4:    printer.printlnInstruction('i4load', ['[i4+' + avtIntToString(i4.indexAcquire(asInt4) * 16) + ']']);
                    AVT_INT8:    printer.printlnInstruction('i8load', ['[i8+' + avtIntToString(i8.indexAcquire(asInt8) * 32) + ']']);
                    AVT_LONG:    printer.printlnInstruction('lload', ['[l+' + avtIntToString(l.indexAcquire(asLong) * 8) + ']']);
                    AVT_LONG2:   printer.printlnInstruction('l2load', ['[l2+' + avtIntToString(l2.indexAcquire(asLong2) * 16) + ']']);
                    AVT_LONG4:   printer.printlnInstruction('l4load', ['[l4+' + avtIntToString(l4.indexAcquire(asLong4) * 32) + ']']);
                    AVT_LONG8:   printer.printlnInstruction('l8load', ['[l8+' + avtIntToString(l8.indexAcquire(asLong8) * 64) + ']']);
                    AVT_FLOAT:   printer.printlnInstruction('fload', ['[f+' + avtIntToString(f.indexAcquire(asFloat) * 4) + ']']);
                    AVT_FLOAT2:  printer.printlnInstruction('f2load', ['[f2+' + avtIntToString(f2.indexAcquire(asFloat2) * 8) + ']']);
                    AVT_FLOAT4:  printer.printlnInstruction('f4load', ['[f4+' + avtIntToString(f4.indexAcquire(asFloat4) * 16) + ']']);
                    AVT_FLOAT8:  printer.printlnInstruction('f8load', ['[f8+' + avtIntToString(f8.indexAcquire(asFloat8) * 32) + ']']);
                    AVT_DOUBLE:  printer.printlnInstruction('dload', ['[d+' + avtIntToString(d.indexAcquire(asDouble) * 8) + ']']);
                    AVT_DOUBLE2: printer.printlnInstruction('d2load', ['[d2+' + avtIntToString(d2.indexAcquire(asDouble2) * 16) + ']']);
                    AVT_DOUBLE4: printer.printlnInstruction('d4load', ['[d4+' + avtIntToString(d4.indexAcquire(asDouble4) * 32) + ']']);
                    AVT_DOUBLE8: printer.printlnInstruction('d8load', ['[d8+' + avtIntToString(d8.indexAcquire(asDouble8) * 64) + ']']);
                    end;
                end;
            end;
        end;
        GET_FIELD: begin
            with node.operand1 as AVTField do begin
                if parentType.isStruct() and valueType.isPrimitive() then begin
                    printer.printlnInstruction('getstructfield' + typeToInstructionName(valueType), [fasmStructName]);
                end else begin
                    printer.printlnInstruction('getfield' + typeToInstructionName(valueType), [fasmFullName]);
                end;
            end;
        end;
        GET_PROPERTY: begin
            with node.operand1 as AVTProperty do begin
                interactMember := readMember;
                if isOverridden() or hasIndex or (interactMember = nil) or (interactMember is AVTConstant) then with readSynthetic do begin
                    vindex := virtualIndex;
                    sindex := serviceIndex;
                    if sindex >= 0 then begin
                        printer.printlnInstruction('invokeservice' + typeReturnSuffix(returnType), [parentType.fasmFullName, fasmShortName, avtIntToString(sindex)]);
                    end else
                    if vindex >= 0 then begin
                        printer.printlnInstruction('invokevirtual' + typeReturnSuffix(returnType), [fasmFullName, avtIntToString(vindex)]);
                    end else begin
                        printer.printlnInstruction('invokespecial' + typeReturnSuffix(returnType), [fasmFullName]);
                    end;
                end else
                if interactMember is AVTMethod then with AVTMethod(interactMember) do begin
                    vindex := virtualIndex;
                    sindex := serviceIndex;
                    if sindex >= 0 then begin
                        printer.printlnInstruction('invokeservice' + typeReturnSuffix(returnType), [parentType.fasmFullName, fasmShortName, avtIntToString(sindex)]);
                    end else
                    if vindex >= 0 then begin
                        printer.printlnInstruction('invokevirtual' + typeReturnSuffix(returnType), [fasmFullName, avtIntToString(vindex)]);
                    end else begin
                        printer.printlnInstruction('invokespecial' + typeReturnSuffix(returnType), [fasmFullName]);
                    end;
                end else
                if interactMember is AVTField then with AVTField(interactMember) do begin
                    printer.printlnInstruction('getfield' + typeToInstructionName(valueType), [fasmFullName]);
                end;
            end;
        end;
        GET_ARRAY_ELEMENT: begin
            printer.printlnInstruction('getarrayelement' + typeToInstructionName(node.operand1 as AVTType));
        end;
        GET_VECTOR_ELEMENT: begin
            printer.printlnInstruction('getvectorelement' + typeToInstructionName(node.operand1 as AVTType));
        end;
        DUP1: begin
            printer.printlnInstruction('dup1');
        end;
        DUP2: begin
            printer.printlnInstruction('dup2');
        end;
        DUP1X1: begin
            printer.printlnInstruction('dup1x1');
        end;
        DUP1X2: begin
            printer.printlnInstruction('dup1x2');
        end;
        POP1: begin
            printer.printlnInstruction('pop1');
        end;
        INVOKE_STATIC: begin
            with node.operand1 as AVTMethod do begin
                printer.printlnInstruction('invokestatic' + typeReturnSuffix(returnType), [fasmFullName]);
            end;
        end;
        INVOKE_SPECIAL: begin
            with node.operand1 as AVTMethod do begin
                printer.printlnInstruction('invokespecial' + typeReturnSuffix(returnType), [fasmFullName]);
            end;
        end;
        INVOKE_VIRTUAL: begin
            with node.operand1 as AVTMethod do begin
                vindex := virtualIndex;
                if vindex < 0 then begin
                    printer.printlnInstruction('invokespecial' + typeReturnSuffix(returnType), [fasmFullName]);
                end else begin
                    printer.printlnInstruction('invokevirtual' + typeReturnSuffix(returnType), [fasmFullName, avtIntToString(vindex)]);
                end;
            end;
        end;
        INVOKE_SERVICE: begin
            with node.operand1 as AVTMethod do begin
                printer.printlnInstruction('invokeservice' + typeReturnSuffix(returnType), [parentType.fasmFullName, fasmShortName, avtIntToString(serviceIndex)]);
            end;
        end;
        NEW_ARRAY_BY_LENGTH: begin
            printer.printlnInstruction('newarray', [(node.operand1 as AVTType).fasmFullName]);
        end;
        NEW_MULTI_ARRAY: begin
            printer.printlnInstruction('newmultiarray', [(node.operand1 as AVTType).fasmFullName]);
        end;
        NEW_ARRAY_BY_ELEMENTS: begin
            with node.operand1 as AVTType, node.operand2 as AVTConstant, value do begin
                case (valueType as AVTTypePrimitive).primitiveKind of
                AVT_BYTE:  printer.printlnInstruction('bload', [avtByteToString(asByte)]);
                AVT_SHORT: printer.printlnInstruction('sload', [avtShortToString(asShort)]);
                else       printer.printlnInstruction('iload', [avtIntToString(asInt)]);
                end;
                printer.printlnInstruction('newarray', [fasmFullName]);
            end;
        end;
        NEW_VECTOR: begin
            printer.printlnInstruction('newvector' + typeToInstructionName(node.operand1 as AVTType));
        end;
        NEW_CLASS, NEW_STRUCT, NEW_SERVICE: begin
            printer.printlnInstruction('newclass', [(node.operand1 as AVTType).fasmFullName]);
        end;
        OPER_INCREMENT: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'inc', [fasmShortName]);
            end;
        end;
        OPER_DECREMENT: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'dec', [fasmShortName]);
            end;
        end;
        OPER_INC_POST: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'incpost', [fasmShortName]);
            end;
        end;
        OPER_DEC_POST: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'decpost', [fasmShortName]);
            end;
        end;
        OPER_INC_PRED: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'incpred', [fasmShortName]);
            end;
        end;
        OPER_DEC_PRED: begin
            with node.operand1 as AVTVariable do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'decpred', [fasmShortName]);
            end;
        end;
        OPER_TYPE_CAST: begin
            { На будущее: улучшить конверсию примитивных типов.
              Сейчас: сперва изменяется количество элементов вектора, а затем – тип элементов
              Необходимо: если количество элементов вектора понижается, то сделать как сейчас, иначе – наоборот (сперва изменяется тип элементов, а затем – их количество). }
            typeOldRef := node.operand2 as AVTType;
            typeNewRef := node.operand1 as AVTType;
            if (typeOldRef is AVTTypePrimitive) and (typeNewRef is AVTTypePrimitive) then begin
                primOldRef := AVTTypePrimitive(nonStackableTypeToStackable(typeOldRef));
                primNewRef := AVTTypePrimitive(typeNewRef);
                if primOldRef <> primNewRef then begin
                    a := primOldRef.compoundBase;
                    b := primNewRef.compoundBase;
                    if a = AVT_REAL then begin
                        if (b = AVT_BYTE) or (b = AVT_SHORT) or (b = AVT_CHAR) then begin
                            printer.printlnInstruction('xtoi');
                            primOldRef := getPrimitiveType(AVT_INT);
                            a := AVT_INT;
                        end;
                        if (b <> a) and (b <> AVT_BYTE) and (b <> AVT_SHORT) and (b <> AVT_CHAR) then begin
                            primImmRef := getPrimitiveType(b);
                            printer.printlnInstruction(typeToInstructionName(primOldRef) + 'to' + typeToInstructionName(primImmRef));
                            primOldRef := AVTTypePrimitive(nonStackableTypeToStackable(primImmRef));
                            a := primOldRef.compoundBase;
                        end;
                    end;
                    if primOldRef.compoundLength <> primNewRef.compoundLength then begin
                        if b < AVT_BYTE then begin
                            primImmRef := getPrimitiveType(a);
                        end else begin
                            primImmRef := getPrimitiveType(a + (primNewRef.primitiveKind and 3));
                        end;
                        printer.printlnInstruction(typeToInstructionName(primOldRef) + 'to' + typeToInstructionName(primImmRef));
                        primOldRef := AVTTypePrimitive(nonStackableTypeToStackable(primImmRef));
                        a := primOldRef.compoundBase;
                    end;
                    if a <> b then begin
                        case b of
                        AVT_REAL: begin
                            printer.printlnInstruction(typeToInstructionName(primOldRef) + 'tox');
                        end;
                        AVT_CHAR: begin
                            if a <> AVT_INT then begin
                                printer.printlnInstruction(typeToInstructionName(primOldRef) + 'toi');
                            end;
                            printer.printlnInstruction('itoc');
                        end
                        else
                            if ((a = AVT_BYTE) or (a = AVT_SHORT)) and ((b = AVT_FLOAT) or (b = AVT_DOUBLE)) or ((a = AVT_FLOAT) or (a = AVT_DOUBLE)) and ((b = AVT_BYTE) or (b = AVT_SHORT)) then begin
                                primImmRef := getPrimitiveType(AVT_INT + (primNewRef.primitiveKind and 3));
                                printer.printlnInstruction(typeToInstructionName(primOldRef) + 'to' + typeToInstructionName(primImmRef));
                                primOldRef := primImmRef;
                            end;
                            printer.printlnInstruction(typeToInstructionName(primOldRef) + 'to' + typeToInstructionName(primNewRef));
                        end;
                    end;
                end;
            end else
            if (typeOldRef is AVTTypeStructured) and (typeNewRef is AVTTypeStructured) and not typeNewRef.isAssignableFrom(typeOldRef) then begin
                printer.printlnInstruction('rcast', [typeNewRef.fasmFullName]);
            end;
        end;
        OPER_BIT_NOT: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'not');
        end;
        OPER_BIT_AND: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'and');
        end;
        OPER_BIT_OR: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'or');
        end;
        OPER_BIT_XOR: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'xor');
        end;
        OPER_SCAL_MINUS: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'sneg');
        end;
        OPER_SCAL_MUL: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'smul');
        end;
        OPER_SCAL_DIV: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'sdiv');
        end;
        OPER_SCAL_DIVU: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'sdivu');
        end;
        OPER_SCAL_REM: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'srem');
        end;
        OPER_SCAL_REMU: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'sremu');
        end;
        OPER_SCAL_ADD: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'sadd');
        end;
        OPER_SCAL_SUB: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'ssub');
        end;
        OPER_SCAL_SAR: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'ssar');
        end;
        OPER_SCAL_SAL: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'ssal');
        end;
        OPER_SCAL_SHR: begin
            printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(node.operand1 as AVTType)) + 'sshr');
        end;
        OPER_SCAL_G: begin
            prim := node.operand1 as AVTTypePrimitive;
            kind := prim.compoundBase;
            if (kind = AVT_FLOAT) or (kind = AVT_DOUBLE) or (kind = AVT_REAL) then begin
                printer.printlnInstruction(typeToInstructionName(prim) + 'cmpl');
            end else begin
                printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(prim)) + 'cmp');
            end;
            if (jumpIsTrue = nil) or (jumpIsFalse = nil) then begin
                printer.printlnInstruction('isetg');
                exit;
            end;
            if jumpIsFalse.order = nextOrder then begin
                printer.printlnInstruction('ijmpg', [labelNumberToString(jumpIsTrue.labelNumber)]);
                exit;
            end;
            if jumpIsTrue.order = nextOrder then begin
                printer.printlnInstruction('ijmpng', [labelNumberToString(jumpIsFalse.labelNumber)]);
                exit;
            end;
            printer.printlnInstruction('ijmpg', [labelNumberToString(jumpIsTrue.labelNumber)]);
            printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
            exit;
        end;
        OPER_SCAL_GE: begin
            prim := node.operand1 as AVTTypePrimitive;
            kind := prim.compoundBase;
            if (kind = AVT_FLOAT) or (kind = AVT_DOUBLE) or (kind = AVT_REAL) then begin
                printer.printlnInstruction(typeToInstructionName(prim) + 'cmpl');
            end else begin
                printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(prim)) + 'cmp');
            end;
            if (jumpIsTrue = nil) or (jumpIsFalse = nil) then begin
                printer.printlnInstruction('isetge');
                exit;
            end;
            if jumpIsFalse.order = nextOrder then begin
                printer.printlnInstruction('ijmpge', [labelNumberToString(jumpIsTrue.labelNumber)]);
                exit;
            end;
            if jumpIsTrue.order = nextOrder then begin
                printer.printlnInstruction('ijmpnge', [labelNumberToString(jumpIsFalse.labelNumber)]);
                exit;
            end;
            printer.printlnInstruction('ijmpge', [labelNumberToString(jumpIsTrue.labelNumber)]);
            printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
            exit;
        end;
        OPER_SCAL_L: begin
            prim := node.operand1 as AVTTypePrimitive;
            kind := prim.compoundBase;
            if (kind = AVT_FLOAT) or (kind = AVT_DOUBLE) or (kind = AVT_REAL) then begin
                printer.printlnInstruction(typeToInstructionName(prim) + 'cmpg');
            end else begin
                printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(prim)) + 'cmp');
            end;
            if (jumpIsTrue = nil) or (jumpIsFalse = nil) then begin
                printer.printlnInstruction('isetl');
                exit;
            end;
            if jumpIsFalse.order = nextOrder then begin
                printer.printlnInstruction('ijmpl', [labelNumberToString(jumpIsTrue.labelNumber)]);
                exit;
            end;
            if jumpIsTrue.order = nextOrder then begin
                printer.printlnInstruction('ijmpnl', [labelNumberToString(jumpIsFalse.labelNumber)]);
                exit;
            end;
            printer.printlnInstruction('ijmpl', [labelNumberToString(jumpIsTrue.labelNumber)]);
            printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
            exit;
        end;
        OPER_SCAL_LE: begin
            prim := node.operand1 as AVTTypePrimitive;
            kind := prim.compoundBase;
            if (kind = AVT_FLOAT) or (kind = AVT_DOUBLE) or (kind = AVT_REAL) then begin
                printer.printlnInstruction(typeToInstructionName(prim) + 'cmpg');
            end else begin
                printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(prim)) + 'cmp');
            end;
            if (jumpIsTrue = nil) or (jumpIsFalse = nil) then begin
                printer.printlnInstruction('isetle');
                exit;
            end;
            if jumpIsFalse.order = nextOrder then begin
                printer.printlnInstruction('ijmple', [labelNumberToString(jumpIsTrue.labelNumber)]);
                exit;
            end;
            if jumpIsTrue.order = nextOrder then begin
                printer.printlnInstruction('ijmpnle', [labelNumberToString(jumpIsFalse.labelNumber)]);
                exit;
            end;
            printer.printlnInstruction('ijmple', [labelNumberToString(jumpIsTrue.labelNumber)]);
            printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
            exit;
        end;
        OPER_SCAL_E: begin
            typ := node.operand1 as AVTType;
            if typ is AVTTypeStructured then begin
                if node[0].isLoadNull() or node[1].isLoadNull() then begin
                    printer.printlnInstruction('risnull');
                end else begin
                    printer.printlnInstruction('re');
                end;
            end else
            if typ.isCompound() then begin
                printer.printlnInstruction(typeToInstructionName(typ) + 'se');
            end else begin
                prim := node.operand1 as AVTTypePrimitive;
                kind := prim.compoundBase;
                if (kind = AVT_FLOAT) or (kind = AVT_DOUBLE) or (kind = AVT_REAL) then begin
                    printer.printlnInstruction(typeToInstructionName(prim) + 'cmpl');
                end else begin
                    printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(prim)) + 'cmp');
                end;
                if (jumpIsTrue = nil) or (jumpIsFalse = nil) then begin
                    printer.printlnInstruction('isete');
                    exit;
                end;
                if jumpIsFalse.order = nextOrder then begin
                    printer.printlnInstruction('ijmpe', [labelNumberToString(jumpIsTrue.labelNumber)]);
                    exit;
                end;
                if jumpIsTrue.order = nextOrder then begin
                    printer.printlnInstruction('ijmpne', [labelNumberToString(jumpIsFalse.labelNumber)]);
                    exit;
                end;
                printer.printlnInstruction('ijmpe', [labelNumberToString(jumpIsTrue.labelNumber)]);
                printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
                exit;
            end;
        end;
        OPER_SCAL_NE: begin
            typ := node.operand1 as AVTType;
            if typ is AVTTypeStructured then begin
                if node[0].isLoadNull() or node[1].isLoadNull() then begin
                    printer.printlnInstruction('risnotnull');
                end else begin
                    printer.printlnInstruction('rne');
                end;
            end else
            if typ.isCompound() then begin
                printer.printlnInstruction(typeToInstructionName(typ) + 'sne');
            end else begin
                prim := node.operand1 as AVTTypePrimitive;
                kind := prim.compoundBase;
                if (kind = AVT_FLOAT) or (kind = AVT_DOUBLE) or (kind = AVT_REAL) then begin
                    printer.printlnInstruction(typeToInstructionName(prim) + 'cmpl');
                end else begin
                    printer.printlnInstruction(typeToInstructionName(nonStackableTypeToStackable(prim)) + 'cmp');
                end;
                if (jumpIsTrue = nil) or (jumpIsFalse = nil) then begin
                    printer.printlnInstruction('isetne');
                    exit;
                end;
                if jumpIsFalse.order = nextOrder then begin
                    printer.printlnInstruction('ijmpne', [labelNumberToString(jumpIsTrue.labelNumber)]);
                    exit;
                end;
                if jumpIsTrue.order = nextOrder then begin
                    printer.printlnInstruction('ijmpe', [labelNumberToString(jumpIsFalse.labelNumber)]);
                    exit;
                end;
                printer.printlnInstruction('ijmpne', [labelNumberToString(jumpIsTrue.labelNumber)]);
                printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
                exit;
            end;
        end;
        OPER_VECT_PACK: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vpack');
        end;
        OPER_VECT_UNPCKL: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vunpckl');
        end;
        OPER_VECT_UNPCKU: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vunpcku');
        end;
        OPER_VECT_MINUS: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vneg');
        end;
        OPER_VECT_MUL: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vmul');
        end;
        OPER_VECT_DIV: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vdiv');
        end;
        OPER_VECT_ADD: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vadd');
        end;
        OPER_VECT_SUB: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vsub');
        end;
        OPER_VECT_SAR: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vsar');
        end;
        OPER_VECT_SAL: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vsal');
        end;
        OPER_VECT_SHR: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vshr');
        end;
        OPER_VECT_G: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vg');
        end;
        OPER_VECT_GE: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vge');
        end;
        OPER_VECT_L: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vl');
        end;
        OPER_VECT_LE: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vle');
        end;
        OPER_VECT_E: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 've');
        end;
        OPER_VECT_NE: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vne');
        end;
        OPER_VECT_MULS: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vmuls');
        end;
        OPER_VECT_ADDS: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vadds');
        end;
        OPER_VECT_SUBS: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vsubs');
        end;
        OPER_VECT_MULU: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vmulu');
        end;
        OPER_VECT_ADDU: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vaddu');
        end;
        OPER_VECT_SUBU: begin
            printer.printlnInstruction(typeToInstructionName(node.operand1 as AVTType) + 'vsubu');
        end;
        STORE_STATIC: begin
            with node.operand1 as AVTField do begin
                printer.printlnInstruction(typeToInstructionName(valueType) + 'store', ['[' + fasmFullName + ']']);
            end;
        end;
        SET_FIELD: begin
            with node.operand1 as AVTField do begin
                if parentType.isStruct() and valueType.isPrimitive() then begin
                    printer.printlnInstruction('setstructfield' + typeToInstructionName(valueType), [fasmStructName]);
                end else begin
                    printer.printlnInstruction('setfield' + typeToInstructionName(valueType), [fasmFullName]);
                end;
            end;
        end;
        SET_PROPERTY: begin
            with node.operand1 as AVTProperty do begin
                interactMember := writeMember;
                if isOverridden() or hasIndex or (interactMember = nil) then with writeSynthetic do begin
                    vindex := virtualIndex;
                    sindex := serviceIndex;
                    if sindex >= 0 then begin
                        printer.printlnInstruction('invokeservice', [parentType.fasmFullName, fasmShortName, avtIntToString(sindex)]);
                    end else
                    if vindex >= 0 then begin
                        printer.printlnInstruction('invokevirtual', [fasmFullName, avtIntToString(vindex)]);
                    end else begin
                        printer.printlnInstruction('invokespecial', [fasmFullName]);
                    end;
                end else
                if interactMember is AVTMethod then with AVTMethod(interactMember) do begin
                    vindex := virtualIndex;
                    sindex := serviceIndex;
                    if sindex >= 0 then begin
                        printer.printlnInstruction('invokeservice', [parentType.fasmFullName, fasmShortName, avtIntToString(sindex)]);
                    end else
                    if vindex >= 0 then begin
                        printer.printlnInstruction('invokevirtual', [fasmFullName, avtIntToString(vindex)]);
                    end else begin
                        printer.printlnInstruction('invokespecial', [fasmFullName]);
                    end;
                end else
                if interactMember is AVTField then with AVTField(interactMember) do begin
                    printer.printlnInstruction('setfield' + typeToInstructionName(valueType), [fasmFullName]);
                end;
            end;
        end;
        SET_ARRAY_ELEMENT: begin
            printer.printlnInstruction('setarrayelement' + typeToInstructionName(node.operand1 as AVTType));
        end
        else
            printer.printlnInstruction('; <invalid>');
        end;
        typ := node.dataType;
        if (typ <> nil) and typ.isBoolean() and (jumpIsTrue <> nil) and (jumpIsFalse <> nil) then begin
            if jumpIsFalse.order = nextOrder then begin
                printer.printlnInstruction('ijmpt', [labelNumberToString(jumpIsTrue.labelNumber)]);
            end else
            if jumpIsTrue.order = nextOrder then begin
                printer.printlnInstruction('ijmpf', [labelNumberToString(jumpIsFalse.labelNumber)]);
            end else begin
                printer.printlnInstruction('ijmpt', [labelNumberToString(jumpIsTrue.labelNumber)]);
                printer.printlnInstruction('jmp', [labelNumberToString(jumpIsFalse.labelNumber)]);
            end;
        end;
    end;

    function AVTCodeGenerator.nonStackableTypeToStackable(typeRef: AVTType): AVTType;
    var
        kind: int;
    begin
        if typeRef is AVTTypePrimitive then begin
            kind := AVTTypePrimitive(typeRef).primitiveKind;
            if (kind = AVT_BOOLEAN) or (kind = AVT_CHAR) or (kind = AVT_BYTE) or (kind = AVT_SHORT) then begin
                typeRef := getPrimitiveType(AVT_INT);
            end;
        end;
        result := typeRef;
    end;

    constructor AVTCodeGenerator.create(programme: AVTProgramme);
    begin
        inherited create(programme);
        self.i2 := AVTInt2Storage.create();
        self.i4 := AVTInt4Storage.create();
        self.i8 := AVTInt8Storage.create();
        self.l := AVTLongStorage.create();
        self.l2 := AVTLong2Storage.create();
        self.l4 := AVTLong4Storage.create();
        self.l8 := AVTLong8Storage.create();
        self.f := AVTFloatStorage.create();
        self.f2 := AVTFloat2Storage.create();
        self.f4 := AVTFloat4Storage.create();
        self.f8 := AVTFloat8Storage.create();
        self.d := AVTDoubleStorage.create();
        self.d2 := AVTDouble2Storage.create();
        self.d4 := AVTDouble4Storage.create();
        self.d8 := AVTDouble8Storage.create();
        self.b2 := AVTByte2Storage.create();
        self.b4 := AVTByte4Storage.create();
        self.b8 := AVTByte8Storage.create();
        self.s2 := AVTShort2Storage.create();
        self.s4 := AVTShort4Storage.create();
        self.s8 := AVTShort8Storage.create();
        self.x := AVTRealStorage.create();
        self.strings := AVTStringStorage.create();
        self.debugInfoSources := AVTStringStorage.create();
        self.debugInfoStrings := AVTStringStorage.create();
    end;

    destructor AVTCodeGenerator.destroy;
    begin
        i2.free();
        i4.free();
        i8.free();
        l.free();
        l2.free();
        l4.free();
        l8.free();
        f.free();
        f2.free();
        f4.free();
        f8.free();
        d.free();
        d2.free();
        d4.free();
        d8.free();
        b2.free();
        b4.free();
        b8.free();
        s2.free();
        s4.free();
        s8.free();
        x.free();
        strings.free();
        debugInfoSources.free();
        debugInfoStrings.free();
        inherited destroy;
    end;

    function AVTCodeGenerator.generate(vfs: WriteableVirtualFileSystem; const directory: UnicodeString): UnicodeString;
    var
        isDir: boolean;
        i: int;
        lim: int;
        dstDir: UnicodeString;
        asmDir: UnicodeString;
        avtDir: UnicodeString;
        dstCodeDir: UnicodeString;
        dstConstDir: UnicodeString;
        dstDataDir: UnicodeString;
        dstDbinfDir: UnicodeString;
        dstResDir: UnicodeString;
        dstResPath: UnicodeString;
        asmCodeDir: UnicodeString;
        asmConstDir: UnicodeString;
        asmDataDir: UnicodeString;
        asmResDir: UnicodeString;
        avtResPath: UnicodeString;
        codeDir: UnicodeString;
        dataDir: UnicodeString;
        progFile: UnicodeString;
        name: UnicodeString;
        resList: Hashtable;
        packList: Vector;
        pack: AVTPackage;
        next: AVTPackage;
        enum: FileEnumeration;
    begin
        dstDir := directory + 'dst';
        asmDir := directory + 'src.asm/';
        avtDir := directory + 'src.avt/';
        { 1. Подготовка папки dst }
        enum := vfs.findFirst(dstDir);
        if enum = nil then begin
            vfs.createDirectory(dstDir);
        end else begin
            isDir := enum.isDirectory();
            enum.close();
            if isDir then begin
                clearDirectory(vfs, dstDir + '/');
            end else begin
                vfs.deleteFile(dstDir); { На будущее: файл dst нужно переименовывать, а не удалять. }
                vfs.createDirectory(dstDir);
            end;
        end;
        dstDir := dstDir + '/';
        dstCodeDir := dstDir + 'code';
        dstConstDir := dstDir + 'const';
        dstDataDir := dstDir + 'data';
        dstDbinfDir := dstDir + 'dbinf';
        dstResDir := dstDir + 'rsrc';
        asmCodeDir := asmDir + 'code/';
        asmConstDir := asmDir + 'const/';
        asmDataDir := asmDir + 'data/';
        asmResDir := asmDir + 'rsrc/';
        { 2. Создание подпапок }
        vfs.createDirectory(dstCodeDir);
        vfs.createDirectory(dstConstDir);
        vfs.createDirectory(dstDataDir);
        vfs.createDirectory(dstDbinfDir);
        vfs.createDirectory(dstResDir);
        dstCodeDir := dstCodeDir + '/';
        dstConstDir := dstConstDir + '/';
        dstDataDir := dstDataDir + '/';
        dstDbinfDir := dstDbinfDir + '/';
        dstResDir := dstResDir + '/';
        { 3. Копирование файлов }
        copyFilesOnly(vfs, asmDir, dstDir);
        copyFilesOnly(vfs, asmCodeDir, dstCodeDir);
        copyFilesOnly(vfs, asmConstDir, dstConstDir);
        copyFilesOnly(vfs, asmDataDir, dstDataDir);
        copyFilesOnly(vfs, asmResDir, dstResDir);
        { 4. Компиляция пакетов }
        progFile := dstDir + 'programme.asm';
        resList := nil;
        packList := Vector.create();
        try
            with programme.packages() do while hasMoreElements() do begin
                pack := nextElement().objectValue() as AVTPackage;
                if pack.types().hasMoreElements() then begin
                    if length(pack.fullName) <= 0 then begin
                        packList.insert(0, ValueOfObject.create(pack, false));
                    end else begin
                        packList.append(ValueOfObject.create(pack, false));
                    end;
                end;
            end;
            next := packList.firstElement().objectValue() as AVTPackage;
            lim := packList.size() - 1;
            for i := 0 to lim do begin
                pack := next;
                if i >= lim then begin
                    next := nil;
                end else begin
                    next := packList.elementAt(i + 1).objectValue() as AVTPackage;
                end;
                name := stringToUTF16(pack.fullName);
                if length(name) <= 0 then begin
                    name := 'system.package';
                end;
                codeDir := dstCodeDir + name;
                dataDir := dstDataDir + name;
                vfs.createDirectory(codeDir);
                vfs.createDirectory(dataDir);
                compilePackage(pack, next, vfs, codeDir + '/', dataDir + '/', asmCodeDir + name + '/');
            end;
            { 5. Генерация файла const/programme.inc }
            compileProgrammeConstants(vfs, dstConstDir + 'programme.inc');
            { 6. Генерация отладочной информации }
            compileProgrammeDebugInfo(vfs, dstDbinfDir);
            { 7. Копирование ресурсов }
            resList := listResources(vfs, avtDir);
            with resList.keys() do while hasMoreElements() do begin
                name := nextElement().unicodeStringValue();
                avtResPath := avtDir + name;
                dstResPath := dstResDir + resourceDirToDir(name);
                if length(name) > 0 then begin
                    enum := vfs.findFirst(dstResPath);
                    if enum = nil then begin
                        vfs.createDirectory(dstResPath);
                    end else begin
                        enum.close();
                    end;
                    dstResPath := dstResPath + '/';
                end;
                with (resList.get(ValueOfUnicodeString.create(name)).objectValue() as Vector).elements() do while hasMoreElements() do begin
                    name := nextElement().unicodeStringValue();
                    copyFile(vfs, avtResPath + name, dstResPath + name);
                end;
            end;
            { 8. Генерация файла programme.asm }
            compileProgramme(vfs, progFile, packList, resList);
        finally
            packList.free();
            resList.free();
        end;
        result := progFile;
    end;
{%endregion}

end.