Реферат: Ассемблер для платформы Java

/>Содержание.

Содержание. 1

Содержание. 2

Введение. 3

Постановка задачи. 4

Формат файла класса. 5

Структура файла класса. 5

Типы элементов Constant_pool 6

Формат структуры field_info. 7

Формат структуры method_info. 7

Формат атрибута Code. 8

Работа JVM… 10

Система команд JVM. 12

Синтаксис языка ассемблера для платформы Java (языка JASM). 14

Тестовые примеры. 18

1. 18

2. 19

3. 20

Проектирование и реализация компилятора. 25

Заключение. 30

Использованная литература. 31


           Введение.

Язык программирования Java был разработан в середине 90-х годовна основе языка Oak,предназначавшегося для программирования «прошивок» для различных электронныхустройств. Однако, в отличие от своего предшественника, язык Java получил широкое распространение,прежде всего как язык, использовавшийся в программировании для сети Интернет. Внастоящее время область применения Java значительно расширилась, и этот язык часто применяется и в обычномприкладном программировании. Это обусловлено такими преимуществами каккроссплатформенность и безопасность,  которые обеспечиваются тем, что исходныйкод на Java компилируется не непосредственно вмашинный код, а в, так называемый, байт-код, который интерпретируетсявиртуальной машиной Java (JVM). Во многих современных реализациях JVM байт-код перед выполнениемпреобразуется в машинные инструкции, что значительно повышаетпроизводительность, приближая ее к производительности программ, написанных на C/C++. Таким образом, Java, в современном состоянии этой технологии, сочетает преимуществаинтерпретируемых и компилируемых языков программирования.

Спецификация, описывающаяJVM, как абстрактную вычислительнуюмашину, предоставлена компанией Sun воткрытый доступ. Это позволяет создавать как собственные реализации JVM для различных платформ, так иальтернативные компиляторы, генерирующие байт-код Java, в том числе для языков программирования, отличных отJava. Большинство литературы, посвященнойJava, почти не уделяет вниманияустройству JVM и описывает лишь сам язык Java. Однако, в ряде случаев, знаниеособенностей архитектуры бывает весьма полезным. В данной работе я создалучебную программу, которая может помочь в изучении архитектуры JVM – несложный ассемблер для байт-кода Java.


Постановка задачи.

Требуется изучитьархитектуру уровня команд платформы Java, формат файла класса Java,и написать компилятор ассемблероподобного языка, позволяющего создавать файлыклассов, корректно обрабатываемые реальной JVM. Данный компилятор должен поддерживать все командыбайт-кода Java и важнейшие возможности JVM.


Формат файла класса.

Основным форматомисполняемых файлов в архитектуре Javaявляется формат файла класса, описанный в The JavaTM Virtual MachineSpecification, изданной компанией Sun. Файл данного формата имеет имя, совпадающее с идентификатором класса(за исключением вложенных классов) и расширение .class.

Структура файла класса.

Файл класса имеетследующую структуру:

 

ClassFile {

             u4 magic;

             u2 minor_version;

             u2 major_version;

             u2 constant_pool_count;

             cp_info constant_pool[constant_pool_count-1];

             u2 access_flags;

             u2 this_class;

             u2 super_class;

             u2 interfaces_count;

             u2 interfaces[interfaces_count];

             u2 fields_count;

             field_info fields[fields_count];

             u2 methods_count;

             method_info methods[methods_count];

             u2 attributes_count;

             attribute_info attributes[attributes_count];

/>  }

(здесь и далее u1, u2, u4 –целые числа размером 8, 16 и 32 бит с порядком байтов старший байт по младшемуадресу). Рассмотрим последовательно все поля.

·    magic – так называемое магическоечисло, имеющее в шестнадцатеричной записи вид 0xCAFEBABE;

·    minor_version, major_version – версия формата файла, по нейопределяется совместимость данного файла с конкретной версией JVM;

·    constant_pool_count – количествоэлементов в Constant_pool плюсединица;

·    constant_pool – область констант –массив структур переменного размера, представляющих те или иные константныезначения. Обращения в область констант производятся по индексу (индексацияначинается с единицы; индексы, следующие за позициями констант, представляющихчисла типов long и double, неиспользуются). Форматы констант различных видов будут рассмотрены ниже;

·    access_flags – комбинация битовыхфлагов, определяющих права доступа и некоторые другие характеристики класса:

/>Флаг

/>Значение

/>Смысл

/>ACC_PUBLIC

/>0x0001

/>Доступен из-за пределов пакета

/>ACC_FINAL

/>0x0010

/>Запрещено наследование от данного класса

/>ACC_SUPER

/>0x0020

/>В методах данного класса требуется использовать принятую в Java2 трактовку команды invokespecial

/>ACC_INTERFACE

/>0x0200

/>Интерфейс (является классом специального вида)

/>ACC_ABSTRACT

/>0x0400

/>Абстрактный класс

·    this_class, super_class – индексыструктур в области констант, ссылающихся на данный класс и его класс-предок;

·    interfaces_count – число интерфейсов,реализуемых данным классом;

·    interfaces – массив индексов структурв области констант, ссылающихся на интерфейсы, реализуемые данным классом;

·    fields_count – количество полей вданном классе;

·    fields – массив структур field_info,описывающих поля класса. Формат структуры field_info будет рассмотрен ниже;

·    methods_count – количество методов;

·    methods – массив структур method_info,описывающих методы класса. Формат структуры mettho_info будет рассмотрен ниже.Конструкторы и статические инициализаторы представляются методами соспециальными именами <init>и <clinit>;

·    attributes_count – количествоатрибутов класса;

·    attributes – массив структур-атрибутовкласса (поля, методы и байт-код методов также могут иметь свои атрибуты).Каждая такая структура в начале имеет два обязательных поля, описывающих типатрибута и его размер. К классу могут быть применены следующие стандартныеатрибуты: SourceFile – указывает на файл исходноготекста, из которого был получен данный файл класса, и Deprecated – класс оставлен для совместимостисо старым кодом и его использование не рекомендуется. Возможно создание атрибутовнестандартных типов, но они будут игнорироваться средой выполнения.

Типы элементов Constant_pool

Каждый элемент сonstant_pool начинается с однобайтного поля, определяющего еготип. Размер и содержание остальной части структуры зависит от типа. Существуютследующие типы констант (элементов constant_pool):

·    CONSTANT_Class– указывает на класс. Содержит индекс константы типа CONSTANT_Utf8, хранящей дескриптор класса;

·    CONSTANT _Fieldref – указывает на поле класса. Содержит индексыконстант типа CONSTANT_Class и CONSTANT_NameAndType;

·    CONSTANT _Methodref указывает на метод класса (неинтерфейса). Содержит индексы констант типа CONSTANT_Class и CONSTANT_NameAndType;

·    CONSTANT_InterfaceMethodref указывает на метод интерфейса. Содержит индексыконстант типа CONSTANT_Class и CONSTANT_NameAndType;

·    CONSTANT_String – указывает на строку, содержит индекс константы типаCONSTANT_Utf8;

·    CONSTANT_Integer – содержит целое 32-разрядное число;

·    CONSTANT_Float – содержит вещественное число одинарной точности;

·    CONSTANT_Long – содержит целое 64-разрядное число;

·    CONSTANT_Double – содержит вещественное число двойной точности;

·    CONSTANT_NameAndType – описывает сигнатуру и имя методалибо тип и имя поля. Содержит индексы двух констант типа CONSTANT_Utf8, хранящих соответственно имя и дескриптор типа(сигнатуры);

·    CONSTANT_Utf8 – содержит строку в формате Utf8 (символы Unicode представляются комбинациями от 1 до 3-х байт, причем символыс кодами, не превышающими 127, представляются одним байтом).

Дескрипторы – это строки,описывающие типы и сигнатуры методов в компактном формате. Примитивные типыобозначаются одной буквой, типы массивов – открывающими квадратными скобками вколичестве, равном размерности массива, перед обозначением базового типа.Классы описываются строкой, содержащей имя класса с полным путем, при этомвместо точки роль разделителя имен пакетов и класса выполняет слэш. Вдескрипторах сигнатур методов в круглых скобках без разделителей перечисляютсядескрипторы типов параметров; после закрывающей скобки находится дескриптортипа возвращаемого значения. Для устранения неоднозначностей при этом переддескрипторами классов записывается буква L, а после них – точка с запятой. Например, (ILjava/lang/Object;)I – (int, Object):int (буквой I обозначается тип int).

Формат структуры field_info

Структура field_info имеет следующий формат:

field_info {

             u2access_flags;

             u2name_index;

             u2descriptor_index;

             u2attributes_count;

             attribute_infoattributes[attributes_count];

/>                 }

        

         Здесь:

·    access_flags — комбинация битовых флагов, определяющих правадоступа и некоторые другие характерист     ики поля:

Имя флага

/>Значение

/>Смысл

/>ACC_PUBLIC

/>0x0001

/>Поле объявлено как public

/>ACC_PRIVATE

/>0x0002

/>Поле объявлено как private

/>ACC_PROTECTED

/>0x0004

/>Поле объявлено как protected

/>ACC_STATIC

/>0x0008

/>Поле является статическим

/>ACC_FINAL

/>0x0010

/>Поле объявлено как final и не может быть изменено после начальной инициализации

/>ACC_VOLATILE

/>0x0040

/>Поле объявлено как volatile

/>ACC_TRANSIENT

/>0x0080

/>Поле объявлено как transient – не сохранятся при сериализации

·    name_index – индекс строковой константы-имени поля в Constant Pool;

·    descriptor_index – индекс строковой константы-дескриптора поля(описывает тип) в ConstantPool;

·    attributes_count – число атрибутов поля;

·    attributes – атрибуты поля. К полям могут бытьприменены стандартные атрибуты Deprecated (см. выше), Synthetic (полесоздано компилятором и не объявлено явно в исходном тексте) и ConstantValue (инициализирующее значение длястатического поля).

Формат структуры method_info

         Структура method_info имеет следующий формат:

    method_info {

             u2 access_flags;

             u2 name_index;

             u2 descriptor_index;

             u2 attributes_count;

             attribute_info attributes[attributes_count];

/>             }

 

Здесь:

·    access_flags – битовые флаги, определяющие права доступа инекоторые дополнительные свойства метода:

/>Flag Name

/>Value

/>Interpretation

/>ACC_PUBLIC

/>0x0001

/>Метод объявлен как public

/>ACC_PRIVATE

/>0x0002

/>Метод объявлен как private

/>ACC_PROTECTED

/>0x0004

/>Метод объявлен как protected

/>ACC_STATIC

/>0x0008

/>Метод является статическим

/>ACC_FINAL

/>0x0010

/>Метод является финальным и не может быть замещен

/>ACC_SYNCHRONIZED

/>0x0020

/>Метод объявлен как synchronized

/>ACC_NATIVE

/>0x0100

/>Метод является «родным» и содержит код, непосредственно выполняющийся физическим процессором

/>ACC_ABSTRACT

/>0x0400

/>Метод является абстрактным

/>ACC_STRICT

/>0x0800

/>Устанавливает «строгий» режим работы с вещественными числами (только в Java 2).

·    name_index,descriptor_index, attributes_count – аналогично field_info;

·    attributes – атрибуты метода. Методы могутиметь следующие стандартные атрибуты:

o      Deprecated, Synthetic – аналогично соответствующиматрибутам полей;

o      Exceptions – описание исключений, которые можетгенерировать метод. Нужно отметить, что обязательное описание исключений неявляется необходимым требованием для корректного выполнения;

o      Code – собственно говоря, байт-кодметода.

Формат атрибута Code.

Атрибут Code имеет следующую структуру:

 

Code_attribute{

             u2 attribute_name_index;

             u4 attribute_length;

             u2 max_stack;

             u2 max_locals;

             u4 code_length;

             u1 code[code_length];

             u2 exception_table_length;

             {           u2 start_pc;

                         u2 end_pc;

                         u2  handler_pc;

                         u2  catch_type;

             }           exception_table[exception_table_length];

             u2 attributes_count;

             attribute_info attributes[attributes_count];

/>  }

 

Здесь:

·    attribute_name_index,attribute_length – стандартные для любого атрибута поля, описывающиеего тип и размер;

·    max_stack – предельный размер стека операндов для метода;

·    max_locals – предельное количество локальных переменных метода(включая формальные параметры);

·    code_length – размер байт-кода метода в байтах;

·    code – собственно говоря, байт-код;

·    exception_table_length – количество защищенных блоков;

·    exception_table – таблица защищенных блоков (обработчиковисключений). Каждая ее запись имеет следующие поля:

o      start_pc – индекс начала защищенного блока в массиве байт-кода,

o      end_pc – индекс конца защищенного блока,

o      handler_pc – индекс начала обработчика,

o      catch_type – тип обрабатываемого исключения (индекс в Constant Pool) или 0 для блока try… finally;

·    attributes_count– число атрибутов;

·    attributes – атрибуты кода метода. Могутиспользоваться стандартные атрибуты LineNumberTable и LocalVariableTable, содержащие отладочную информацию.


Работа JVM

При запуске JVM в качестве параметров ей передаютсяимя класса, с метода mainкоторого будет начато выполнение программы, а также аргументы командной строкипрограммы. Вначале загружается указанный класс. Другие классы, используемые впрограмме, загружаются при первом обращении к ним. Процесс загрузки классасостоит из нескольких этапов:

·    собственнозагрузка файла класса (loading).По умолчанию осуществляется с помощью класса ClassLoader из стандартной библиотеки Java, однако можно использоватьпользовательский загрузчик для изменения способа поиска файла;

·    связывание (linking). Состоит из трех стадий:

o      проверка (verification) на правильность формата файлакласса и корректность байт-кода (например, на отсутствие переходов на серединуинструкции),

o      подготовка (preparation) – выделение памяти для статическихполей класса и заполнение их нулевыми значениями,

o      разрешение имен (resolution) ;

·    инициализация (initialization) статических данных начальнымизначениями. Включает вызов метода <clinit>, если он присутствует в классе.

Программа, выполняемая JVM, может иметь несколько потоковвыполнения. Реализация многопоточности зависит от используемого аппаратногообеспечения и может быть различной – разные потоки могут выполняться на разныхпроцессорах или им могут выделяться кванты времени на одном процессоре. JVM имеет ряд средств для синхронизацииработы потоков и защиты разделяемых ими данных. Важнейшим из них является механизмблокировок (locks), поддерживаемый на уровне системыкоманд JVM. Каждый объект имеет ассоциированныйс ним «замок» (lock). Если одиниз потоков «закрыл» этот «замок», то ни один другой поток не сможет также его«закрыть» до тех пор, пока первый поток его не «откроет».

JVM определяет несколько виртуальныхобластей памяти, которые она использует при своей работе:

·    регистр PC (program counter), указывающий на текущую позицию выполнения в методе.Каждый поток программы имеет свой регистр PC;

·    стек. Каждыйпоток имеет свой собственный стек. При входе в метод на вершине стека создаетсяфрейм, содержащий локальные переменные метода и его стек операндов. Размерименно этих областей указывается полями max_localsи max_stack атрибута метода Code;

·    куча – областьпамяти, в которой динамически размещаются объекты классов и массивы. Памятьиз-под не используемых более объектов (на которые нет ссылок) автоматическиосвобождается так называемым сборщиком мусора;

·    область методов.В нее при загрузке классов помещаются байт-код методов, различная информация ометодах и полях. Область методов также содержит области констант временивыполнения, которые хранят содержимое constant pool иззагруженных классов;

·    стеки для native-методов.

         Расположение ипредставление этих областей в физической памяти может быть различным вразличных реализациях JVM.

         JVM является стековой машиной.Большинство из команд JVMвыполняют одно из следующих действий:

·    считываютзначение из переменной или поля и помещают его на вершину стека,

·    сохраняютзначение с вершины стека в переменной или поле,

·    выполняют те илииные действия над значениями, взятыми с вершины стека, и записываютрезультирующее значение (если оно есть) на вершину стека,

·    выполняют переходна команду с заданным смещением относительно текущего значения регистра PC безусловно или в зависимости отзначений, прочитанных с вершины стека.

Любое чтение из стекаоперандов приводит к удалению из него прочитанного значения. Размер стекаоперандов, указываемый как max_stack, рассчитывается следующим образом:значения типов long и double занимают две ячейки стека (8 байт),любые другие значения – одну (4 байта). Значения типов char, boolean,byte, short сохраняются в одной четырехбайтной ячейке. Тут можноотметить, что в подавляющем большинстве случаев  JVM не делает различий между логическими значениями ицелыми числами типа int, для средывыполнения не существует отдельного булевского типа (лжи соответствует нулевоезначение, истине – ненулевое, как правило, единица). Однако, в массивах типа boolean[] на каждый элемент выделяется одинбайт. Существует следующее ограничение на байт-код: каждый раз, когда точкавыполнения достигает любой конкретной позиции в методе, глубина стека должнабыть одинаковой, кроме того, тип верхних значений в стеке долженсоответствовать требуемому типу извлекаемых очередной командой значений.

В области локальныхпеременных на момент начала выполнения метода в первых позициях находятсяфактические параметры метода, а в случае метода экземпляра первую (нулевую)позицию занимает ссылка thisна текущий объект. Никакого различия в процессе выполнения метода междупараметрами (даже ссылкой this)и, собственно говоря, локальными переменными не делается. Так же как и в стеке,значения типа long и double в области локальных переменных занимают двечетырехбайтные ячейки, значения типов размером менее 32-х разрядов расширяютсядо четырех байт. В корректном байт-коде должны выполняться, в частности,следующие условия: во-первых, типы значений в локальных переменных должнысоответствовать требуемым для команд, которые обращаются к этим переменным,во-вторых, не допускается чтение значения переменной до ее инициализации(присвоения значения).

Перед вызовом метода егофактические параметры должны находиться на вершине стека; они становятсяоперандами соответствующей команды вызова. При возврате из метода, заисключением методов, возвращающих void, возвращаемое значение помещается на вершину стека.

В процессе выполненияпрограммы в результате возникновения той или иной ошибки либо выполнениякоманды athrow может быть сгенерировано исключение.При этом происходит поиск подходящего обработчика исключения (защищенногоблока) в текущем методе, если он не найден, то в методе, вызвавшем текущий и т.д. Если подходящий обработчик найден, то управление передается в точку,определяемую полем handler_pc соответствующей записи таблицы exception_table в атрибуте Code метода. Ссылка на объект исключения при этом помещается на вершинустека. Объект исключения обязательно должен принадлежать классу Throwable или классу, производному от него.


Система команд JVM.

Первый байт каждойкоманды JVM содержит код операции. Существуетнесколько типичных форматов, которые имеют большинство команд:

·    только кодоперации,

·    код операции иоднобайтный индекс,

·    код операции идвухбайтный индекс,

·    код операции идвухбайтное смещение перехода,

·    код операции ичетырехбайтное смещение перехода.

Несколько командиспользуют другие форматы, среди них две команды переменного размера — tableswitch и lookupswitch. Кроме того, существует специальныйпрефикс wide, который изменяет размер некоторыхкоманд, заменяя однобайтный индекс локальной переменной двухбайтным. В The Java Virtual Machine Specification для каждой команды установлено своемнемоническое обозначение.

Существует много группаналогичных по выполняемому действию команд, работающих с различными типамиданных, например, команды iload,lload, aload, fload,dload выполняют функцию загрузки значенийсоответствующих типов из локальных переменных на вершину стека. Реализациятаких команд может быть идентичной, но он различаются при проверке корректностибайт-кода. Приняты следующие обозначения для типов данных, с которыми работаюткоманды:

·    i — int (также byte, short, char и boolean),

·    l — long,

·    f — float,

·    d — double,

·    a — ссылка на объект или массив.

Кроме того, естьнесколько команд, работающих с типами char, byte и short.

Можно выделить несколькогрупп команд по назначению:

·    команды загрузкии сохранения:

o      Загрузкалокальной переменной на стек: iload, iload_<n>, lload, lload_<n>, fload, fload_<n>, dload, dload_<n>, aload, aload_<n>;

o      Сохранениезначения с вершины стека в локальной переменной: istore, istore_<n>,lstore, lstore_<n>, fstore, fstore_<n>,dstore, dstore_<n>, astore, astore_<n>;

o      Загрузка констант на стек:istore, istore_<n>, lstore, lstore_<n>,fstore, fstore_<n>, dstore, dstore_<n>,astore, astore_<n>;

·    арифметические илогические команды:

o      сложение: iadd, ladd, fadd,dadd;

o      вычитание: isub, lsub,fsub, dsub;

o      умножение:imul, lmul,fmul, dmul;

o      деление:idiv, ldiv,fdiv, ddiv;

o      остаток:irem, lrem,frem, drem;

o      изменение знака:ineg, lneg, fneg, dneg;

o      сдвиги ипобитовые операции: ior, lor, iand, land, ixor, lxor,ishl, ishr, iushr, lshl, lshr, lush;

o      сравнение:dcmpg, dcmpl, fcmpg, fcmpl, lcmg;

o      инкрементлокальной переменной: iinc.

Все эти команды, заисключением iinc, не имеют параметров. Они извлекаютоперанды с вершины стека и записывают результат на вершину стека. Команда iincимеет два операнда — индекс локальнойпеременной и величину, на которую значение данной переменной должно бытьизменено;

·    командыпреобразования типов:

o      расширяющее: i2l, i2f, i2d, l2f,l2d, f2d;

o      сужающее:i2b, i2c, i2s, l2i,f2i, f2l, d2i, d2l, d2f;

·    команды работы собъектами и массивами:

o      создание объекта:new;

o      создание массива:newarray(примитивного типа), anewarray (ссылочного типа), multianewarray(многомерного);

o      доступ к полям: getfield, putfield(для полей экземпляра), getstatic, putstatic (для статических полей);

o      загрузка элементамассива на стек: baload(тип byte), caload (тип char),saload(тип short), iaload, laload, faload, daload, aaload;

o      сохранениезначения с вершины стека в элемент массива: bastore, castore, sastore,iastore, lastore, fastore, dastore, aastore;

o      получение размерамассива: arraylength;

o      проверка типов: instanceof(возвращает на вершине стекалогическое значение) и checkcast (генерирует исключение в случае несоответствия типа ссылки на вершинестека требуемому типу);

·    команды манипуляцийсо стеком операндов:

o      pop — удаление верхнего элемент стека;

o      pop2  - удаление двух верхних элемента стека;

o      dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2 — дублирование элементов на вершинестека;

o      swap — перемена местами двух верхнихэлементов стека;

·    командыбезусловной передачи управления:

o      jsr, jsr_w, retвызов подпрограмм и возврат из них. Используются прикомпиляции блока finally;

o      goto, goto_w — безусловный переход;

·    команды условногоперехода:ifeq, iflt, ifle, ifne, ifgt, ifge, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmplt, if_icmpgt, if_icmple, if_icmpge, if_acmpeq, if_acmpne;

·    команды вызоваметодов:

o      invokevirtual- обычный вызов метода экземпляра с использованиеммеханизма позднего связывания;

o      invokestaticвызов статического метода;

o      invokeinterface - вызов метода интерфейса у объекта,реализующего данный интерфейс;

o      invokespecialвызов метода экземпляра без использования механизмапозднего связывания. Используется для вызова конструкторов, методов суперклассаи private-методов;

·    команды возвратаиз метода:

o      return — возврат из метода, возвращающего void;

o      ireturn, lreturn, freturn, dreturn, areturnвозврат значения соответствующего типа;

·    команда генерацииисключений: athrow;

·    командысинхронизации (механизм блокировок):

o      monitorenter — установить блокировку (войти вкритическую секцию);

o      monitorexit — освободить блокировку (выйти изкритической секции).


Синтаксис языкаассемблера для платформы Java(языка JASM).

Файл исходного текста наязыке ассемблера для платформы Java(языке JASM) представляет собой текстовый файл,строки которого разделены последовательностью символов с кодами 13 и 10. Имяфайла исходного текста и его расширение могут быть любыми, однакорекомендуется, чтобы имя совпадало с именем описанного в файле класса, арасширением было .jasm либо .jsm. Файл исходного текста состоит изпредложений, разделенных точкой с запятой. Последнее предложение может не иметьв конце точки с запятой. Комментарии отделяются знаком процента ираспространяются до конца строки. Точки с запятой и знаки процента внутристроковых констант, ограниченных двойными кавычками, не имеют своегоспециального значения. Две идущие подряд двойные кавычки внутри строковойконстанты интерпретируются как одна двойная кавычка в строке. Любыепоследовательности пробельных символов (пробелов, табуляций, переводов строки ит. д.) интерпретируются как один пробел, если с обеих сторон от них находятсясимволы следующих видов: буквы латинского алфавита, цифры, знак подчеркивания,либо, в противном случае, игнорируются. Исключение составляют пробельныесимволы в строковых константах и комментариях. Верхний и нижний регистр букв видентификаторах, именах команд и других лексемах различается.

Каждый файл исходноготекста компилируется в один файл класса. Файл исходного текста должен иметьследующую структуру:

[модификаторы_доступа]{class|interface}<имя_класса>;

[extends<базовый класс>;]

[implements<интерфейс_1>, <интерфейс_2>,…, <интерфейс_n>;]

[fields;

         <описания_полей>

]

[methods;

         <описания_методов>

]

 

Здесь и далее вквадратные скобки заключены необязательные элементы, в фигурные — альтернативные варианты (разделены вертикальной чертой), в угловые — нетерминальные символы.

Модификаторы_доступа — этослова public, final, abstract, super, соответствующие флагам прав доступаACC_PUBLIC, ACC_FINAL, ACC_ABSTRACT, ACC_STATIC. Эти флаги устанавливаются в единицу тогда и толькотогда, когда в объявлении класса присутствует соответствующее ключевое слово.Класс может иметь несколько различных модификаторов доступа, разделенныхпробелом (или любой другой последовательностью пробельных символов). Повторениеодинаковых модификаторов в заголовке одного класса не допускается. Когда классне имеет флага ACC_INTERFACE, в его объявлении используется словоclass, иначе используется ключевое слово interface. Все имена классов и интерфейсовзаписываются с указанием полного пути (пакетов, в которых эти классысодержатся). Имена пакетов и класса отделяются точкой, например, java.lang.String.В аргументах команд, там, где это необходимо, вместо полного имени текущегокласса можно использовать символ «@». Если базовый класс не указан (предложениеextends отсутствует), то по умолчаниюиспользуется java.lang.Object.Интерфейсы — предки описываемого интерфейса записываются в секции implements.

Для идентификаторов — имен пакетов, классов, полей и методов, а также меток, используются следующиеправила: они должны состоять из букв латинского алфавита любого регистра(регистр имеет значение), знаков подчеркивания и цифр, причем не должныначинаться с цифры. Настоятельно не рекомендуется использованиеидентификаторов, совпадающих с ключевыми словами языка Java, что может привести к некорректной компиляции, либоинтерпретации файлов классов JVM.Два специальных имени <init>и <clinit> также рассматриваются какидентификаторы.

Простейший примерописания класса, не имеющего полей и методов:

publicabstract class some_package.SomeClass;

%это комментарий

extends

         some_package.nested_package1.BaseClass;

implements% и это комментарий

         some_package.Interface1,some_package.nested_package2.Interface2;

         Описание поляимеет следующий вид:

        

[модификаторы_доступа]<имя_поля>:<тип_поля> [=<начальное значение>];

Здесьмодификаторы_доступа — следующие слова: public, protected, private, final, static,transient, volatile, соответствующие флагам доступа поляACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE,ACC_FINAL, ACC_STATIC, ACC_TRANSIENT, ACC_VOLATILE. Повторение одинаковых модификаторовдоступа в объявлении одного поля и сочетания модификаторов, соответствующиезапрещенным сочетаниям флагов доступа (см. The Java Virtual MachineSpecification), вызывают ошибкувремени компиляции. Поляинтерфейса обязательно должны быть объявлены с модификаторами public, static и final.Имя_поля — корректный идентификатор. Тип_поля — имя класса либо имяпримитивного типа (имена примитивных типов совпадают с соответствующимиключевыми словами языка Java — byte, short, int, long, char, float,double, boolean). Начальное значение может быть задано только длястатического поля, если оно указано, то у поля создается атрибут ConstantValue. Начальное значение может бытьцелочисленной, вещественной, логической либо символьной константой для полейсоответствующих типов. Вещественная константа может быть записана в десятичнойлибо экспоненциальной форме, в формате вещественных чисел, принятом в Java. Символьные константы заключаются вапострофы. Кроме того, может быть указан код символа как обычное целое число.Логические константы записываются в виде слов true и false. Примеры описаний полей:

publicfinal static COUNT:int = 10;

staticc:char = ‘A’;

staticc1:char = 13;

privatevolatile m_flag:boolean;

protectedm_list:java.util.ArrayList;

Описание метода в общемслучае имеет вид:

[<модификаторы_доступа>]<имя_метода>(<тип_параметра_1>,<тип_параметра_2>,…,<тип_параметра_n>):<тип_возвращаемого_значения>[throws<класс_исключения_1>,…, <класс_исключения_n>];

%для методов с модификатором abstractнижележащая часть описания

%отсутствует

         maxstack<число>;

         maxlocals<число>;

         [<метка_1>:]      

                   <команда_1>;

                   ...

         [<метка_n>:]

                   <команда_n>;

         [

         protected_blocks;

                   {<класс_исключения>|finally}<метка>: <метка> > <метка>;

                   ...

                   {<класс_исключения>|finally}<метка>: <метка> > <метка>;

]       

         end;

Здесьмодификаторы_доступа — ключевые слова: public, protected, private, static, final,abstract, соответствующие следующим флагамдоступа метода: ACC_PUBLIC, ACC_PRIVATE,ACC_PROTECTED, ACC_STATIC,ACC_FINAL, ACC_ABSTRACT. Повторение одинаковых модификаторовдоступа в заголовке одного метода и сочетания модификаторов, соответствующиезапрещенным сочетаниям флагов доступа (см. The Java Virtual MachineSpecification), вызывают ошибкувремени компиляции. Методыинтерфейса обязательно должны быть объявлены с модификаторами public и abstract. Имя_метода — корректныйидентификатор, либо <init>или <clinit> для конструкторов и статических инициализаторов. Типы параметров и тип возвращаемого значения должны бытьименами классов, либо именами примитивных типов, принятыми в языке Java (byte, short,int, long, char, float, double, boolean).Кроме того, тип возвращаемого значения может быть указан как void. После ключевого слова throws в заголовке метода могут бытьперечислены через запятую имена классов исключений, генерируемых методом. Дляметодов, не являющихся абстрактными, после заголовка обязательно записываютсяпредложения maxstack и maxlocals, в которых указывается размер стекаоперандов и области локальных переменных метода (в четырехбайтных ячейках).Затем следует код метода в виде последовательности команд, разделенных точкамис запятыми. Каждой команде может предшествовать метка, отделяемая от неедвоеточием. Метка должна быть корректным. Каждая команда может иметь не болееодной метки, и каждая метка должна предшествовать той или иной команде. Однако,имеется специальная псевдокоманда none, для которой не генерируется какой-либо код (пустая команда). Ее можноиспользовать, если необходимо расположить более одной метки у одной команды илипоместить метку в конец метода. После ключевого слова protected_blocks могут быть перечислены защищенные блоки (обработчикиисключений) метода. Описание каждого защищенного блока состоит из имени классаисключения или ключевого слова finallyи трех меток, разделенных символами ‘:’ и ‘>’. Первая из них указывает наначало защищенного блока, вторая на его конец, третья — на место в коде метода,куда переходит управление при возникновении исключения или при выходе иззащищенного блока в случае finally.

Используемые в кодемнемонические имена команд совпадают с принятыми в The Java Virtual Machine Specification. Однако, как исключение, префикс wide не рассматривается как отдельнаякоманда, вместо этого команды, его имеющие, записываются как wide_<имя_команды>. Форматы записикоманд:

·         <мнемоническое_имя>; Такую форму имеют следующие команды: aaload, aastore, aconst_null, aload_0, aload_1, aload_2,aload_3 ,areturn, arraylength, astore_0, astore_1, astore_2, astore_3, athrow,baload, bastore, caload, castore, d2f, d2i, d2l, dadd, daload, dastore, dcmpg,dcmpl, dconst_0, dconst_1, ddiv, dload_0, dload_1, dload_2, dload_3, dmul,dneg, drem, dreturn, dstore_0, dstore_1, dstore_2, dstore_3, dsub, dup, dup2,dup2_x1, dup2_x2, dup_x1, dup_x2, f2d, f2i, f2l, fadd, faload, fastore, fcmpg,fcmpl, fconst_0, fconst_1, fconst_2, fdiv, fload_0, fload_1, fload_2, fload_3,fmul, fneg, frem, freturn, fstore_0, fstore_1, fstore_2, fstore_3, fsub, i2b,i2c, i2d, i2f, i2l, i2s, iadd, iaload, iand, iastore, iconst_0, iconst_1,iconst_2, iconst_3, iconst_4, iconst_5, iconst_m1, idiv, iload_0, iload_1,iload_2, iload_3, imul, ineg, ior, irem, ireturn, ishl, ishr, istore_0,istore_1, istore_2, istore_3, isub, iushr, ixor, l2d, l2f, l2i, ladd, laload,land, lastore, lcmp, lconst_0, lconst_1, ldiv, lload_0, lload_1, lload_2,lload_3, lmul, lneg, lor, lrem, lreturn, lshl, lshr, lstore_0, lstore_1,lstore_2, lstore_3, lsub, lushr, lxor, monitorenter, monitorexit, nop, pop,pop2, return, saload, sastore, swap;

·         <мнемоническое_имя> <метка>; Такую форму имеют команды перехода: goto, goto_w, if_acmpeq,if_acmpne, if_acmpge, if_acmpgt, if_icmple, if_icmplt, if_icmpne, ifeq, ifge,ifgt, ifle, iflt, ifne, ifnonull, ifnull, jsr, jsr_w;

·         <мнемоническое_имя> <целоечисло>; Число должно удовлетворять ограничениям конкретной команды: aload, astore, dload, dstore, fload, fstore, iload,istore, lload, lstore, ret, bipush, sipush, wide_aload, wide_astore,wide_dload, wide_dstore, wide_fload, wide_fstore, wide_iload, wide_istore,wide_lload, wide_lstore, wide_ret;

·         <мнемоническое_имя>{<полное_имя_класса>|@}::<имя_поля>:<тип_поля>; Тип_поляимя примитивноготипа, принятое в языке Java,либо имя класса. Команды: getfield, putfield, getstatic, putstatic;

·         <мнемоническое_имя>{<полное_имя_класса>|@}::<имя_метода>(<тип_параметра_1>,…, <тип_параметра_n>):<тип_возвращаемого значения>; Здесь типы параметров и возвращаемогозначения — имена примитивных типов, принятые в языке Java, имена классов, либо (только для возвращаемогозначения) void. Команды: invokespecial, invokestatic,invokevirtual;

·         <мнемоническое_имя><полное_имя_класса>; Такой формат имеют следующие команды: anewarray, checkcast, instanceof, new;

·         <мнемоническое_имя><целое_число_индекс_переменной> <целое_число>; Команды: iinc, wide_iinc;

·         <мнемоническое_имя><тип> <константа>; — команды ldc, ldc_w, ldc_2w. Здесь тип — int, float, string (дляldc, ldc_w), double, long (дляldc_2w). Константа должна иметьсоответствующий тип (целые числа записываются обычным способом, вещественные — в десятичной или экспоненциальной форме, в формате, принятом в Java, строки записываются в двойныхкавычках, при этом две двойные кавычки внутри строки интерпретируются как однадвойная кавычка в строке);

·         invokeinterface<имя_интерфейса>::<имя_метода>(<тип_аргумента_1>,…,<тип_аргумента_2>):<тип_возвращаемого_значения><целое_число>; — типы — аналогично другим командам вызова методов;

·         multianewarray<полное_имя_класса><число_измерений>;

·         newarray{boolean|char|float|double|byte|short|int|long};

·         tableswitch<число_1>:<число_n> default: <метка_0><число_1>:<метка_1>… <число_n>:<метка_n>; Здесь числа число_1… число_n должны быть последовательными целымичислами. При этом числа, указанные сразу после мнемонического имени команды,должны совпадать с границами диапазона чисел, для которых указаны меткиперехода.lookupswitchdefault:<метка_0> <число_1>:<метка_1>…<число_n>:<метка_n>; Здесь среди чисел, для которыхуказаны метки перехода, не должно быть одинаковых. Эти числа должны бытьцелыми, они не обязаны быть упорядочены по возрастанию, сортировка происходитпри обработке команды компилятором.


Тестовые примеры.

Для тестированиякомпилятора использовались, в частности, следующие примеры:

1.

%файлSummator.jsm

publicclass Summator;

fields;

         privatem_i:int;   

methods;

%Конструктор.Заносит в поле m_iцелое число, содержащееся в строке,

%передаваемойв качестве параметра. В случае, если строка не содержит

%правильнойзаписи целого числа, либо это число отрицательное,

%товыводится сообщение об ошибке.

public<init>(java.lang.String):void;

maxstack4;

maxlocals2;

         aload_0;    %this

         dup;

         invokespecial       java.lang.Object::<init>():void;

         aload_1;    %arg1

begin_try:

         invokestatic         java.lang.Integer::parseInt(java.lang.String):int;

         dup;

         iconst_0;

         if_icmpge   end_try;

         new            java.lang.Exception;

         dup;

         invokespecial       java.lang.Exception::<init>():void;

         athrow;

end_try:

         putfield       @::m_i:int;

         return;

exception:

         pop;

         getstatic      java.lang.System::out:java.io.PrintStream;

         ldc             string «Invalid argument»;

         invokevirtual       java.io.PrintStream::println(java.lang.String):void;

         return;

protected_blocks;

         java.lang.Exception

                   begin_try: end_try > exception;

end;

%возвращаетсумму натуральных чисел от 1 до m_i.

publicgetSum():int;

maxstack3;

maxlocals2;

         iconst_0;

         istore_1;

         aload_0;     %this

         getfield       @::m_i:int;

loop:

         dup;

         iload_1;      %result

         iadd;

         istore_1;     %result

         iconst_1;

         isub;

         dup;

         iconst_0;

         if_icmpgt    loop;

         pop;

         iload_1;      %result

         ireturn;

end;

%возвращаетзначение поля m_i

publicgetI():int;

maxstack1;

maxlocals1;

         aload_0;     %this

         getfield       @::m_i:int;

         ireturn;

end;

2.

%файлSwitches.jsm

publicclass Switches;

fields;

methods;

%обаметода функционально эквивалентны следующей функции, написанной на Java.

%static int function(int i) {

%      switch(i){

%                case1: return 2;

%                case2: return -1;

%                default:return 0;

%      }

%}

publicstatic lookup(int):int;

maxstack1;

maxlocals1;

         iload_0;

         lookupswitch

                   default: l_def

                   1      :l_1

                   2      :l_2;

l_def:

         iconst_0;

         ireturn;

l_1:

         iconst_2;

         ireturn;

l_2:

         iconst_m1;

         ireturn;

end;

        

publicstatic table(int):int;

maxstack1;

maxlocals1;

         iload_0;

         tableswitch1:2

                   default: l_def

                   1      :l_1

                   2      :l_2;

l_def:

         iconst_0;

         ireturn;

l_1:

         iconst_2;

         ireturn;

l_2:

         iconst_m1;

         ireturn;

end;

3.

Следующий примерпредставляет собой программу, состоящую из 5 классов.

%-------------------------------------------------------------%

%файлFigure.jsm

publicinterface Figure;

methods;

publicabstract getArea():double;

%-------------------------------------------------------------%

%-------------------------------------------------------------%

%файлCircle.jsm

publicclass Circle;

implementsFigure;

fields;

         privatem_radius:double;

methods;

public<init>(double):void;

maxstack4;

maxlocals3;

         aload_0;

         invokespecial       java.lang.Object::<init>():void;

         dload_1;

         dconst_0;

         dcmpg;

         ifgel_endif;

         newjava.lang.IllegalArgumentException;

         dup;

         invokespecialjava.lang.IllegalArgumentException::<init>():void;

         athrow;

l_endif:

         aload_0;

         dload_1;

         putfield@::m_radius:double;

         return;

end;

publicgetArea():double;

maxstack4;

maxlocals1;

         aload_0;

         getfield@::m_radius:double;

         aload_0;

         getfield@::m_radius:double;

         dmul;

         ldc2_wdouble 3.14159265;

         dmul;

         dreturn;

end;

%-------------------------------------------------------------%

%-------------------------------------------------------------%

%файлRectangle.jsm

publicclass Rectangle;

implementsFigure;

fields;

         privatem_a:double;

         privatem_b:double;

methods;

public<init>(double, double):void;

maxstack4;

maxlocals5;

         aload_0;

         invokespecialjava.lang.Object::<init>():void;

         dload_1;

         dconst_0;

         dcmpl;

         ifltl_error;

         dload_3;

         dconst_0;

         dcmpl;

         ifgel_endif;

l_error:

         newjava.lang.IllegalArgumentException;

         dup;

         invokespecialjava.lang.IllegalArgumentException::<init>():void;

         athrow;

l_endif:

         aload_0;

         dload_1;

         putfield@::m_a:double;

         aload_0;

         dload_3;

         putfield@::m_b:double;

         return;

end;

publicgetArea():double;

maxstack4;

maxlocals1;

         aload_0;

         getfield@::m_a:double;

         aload_0;

         getfield@::m_b:double;

         dmul;

         dreturn;

end;

%-------------------------------------------------------------%

%-------------------------------------------------------------%

%файлSquare.jsm

publicclass Square;

extendsRectangle;

methods;

public<init>(double):void;

maxstack5;

maxlocals3;

         aload_0;

         dload_1;

         dload_1;

         invokespecialRectangle::<init>(double, double):void;

         return;

end;

%-------------------------------------------------------------%

%-------------------------------------------------------------%

%файлMainClass.jsm

publicclass MainClass;

methods;

public<init>():void;

maxstack1;

maxlocals1;

         aload_0;

         invokespecialjava.lang.Object::<init>():void;

         return;

end;

publicstatic main(java.lang.String[]):void;

maxstack8;

maxlocals7;

         iconst_3;

         anewarrayFigure;

         astore_1;

         aload_1;

         iconst_0;

         newCircle;

         dup;

         ldc2_wdouble 10;

         invokespecialCircle::<init>(double):void;

         aastore;

         aload_1;

         iconst_1;

         newRectangle;

         dup;

         dconst_1;

         ldc2_wdouble 2;

         invokespecialRectangle::<init>(double, double):void;

         aastore;

         aload_1;

         iconst_2;

         newSquare;

         dup;

         ldc2_wdouble 3;

         invokespecialSquare::<init>(double):void;

         aastore;

         dconst_0;

         dstore_2;

         iconst_0;

         istore4;

l_50:

         iload4;

         aload_1;

         arraylength;

         if_icmpgel_75;

         dload_2;

         aload_1;

         iload4;

         aaload;

         invokeinterfaceFigure::getArea():double, 1;

         dadd;

         dstore_2;

         iinc4, 1;

         gotol_50;

l_75:

         newjava.io.BufferedReader;

         dup;

         newjava.io.InputStreamReader;

         dup;

         getstaticjava.lang.System::in:java.io.InputStream;

         invokespecialjava.io.InputStreamReader::<init>(java.io.InputStream):void;

         invokespecialjava.io.BufferedReader::<init>(java.io.Reader):void;

         astore4;

l_50:

         aload4;

         invokevirtualjava.io.BufferedReader::readLine():java.lang.String;

         invokestaticjava.lang.Double::parseDouble(java.lang.String):double;

         dstore5;

         getstaticjava.lang.System::out:java.io.PrintStream;

         dload5;

         dload_2;

         dadd;

         invokevirtualjava.io.PrintStream::println(double):void;

l_114:

         gotol_127;

l_117:

         astore4;

         getstaticjava.lang.System::out:java.io.PrintStream;

         ldcstring «Error»;

         invokevirtualjava.io.PrintStream::println(java.lang.String):void;

l_127:

         return;

protected_blocks;

         java.io.IOExceptionl_75: l_114 > l_117;

end;

%-------------------------------------------------------------%

Данная программафункционально эквивалентна следующему коду на Java (ассемблерный вариант создан на основедизассемблированной с помощью утилиты javap Java-программы):

//-----------------------------------------------------------//

publicinterface Figure {

   doublegetArea();

}

//-----------------------------------------------------------//

//-----------------------------------------------------------//

publicclass Circle implements Figure {

   private double m_radius;

   public Circle(double radius) {

       if(radius<0)

           throw new IllegalArgumentException();

       m_radius = radius;

   }

   public double getArea() {

       return m_radius*m_radius*Math.PI;

   }

}

//-----------------------------------------------------------//

//-----------------------------------------------------------//

publicclass Rectangle implements Figure {

   private double m_a;

   private double m_b;

   public Rectangle(double a, double b) {

       if(!((a>=0)&&(b>=0)))

           throw new IllegalArgumentException();

       m_a = a;

       m_b = b;

   }

   public double getArea() {

       return m_a*m_b;

   }

}

//-----------------------------------------------------------//

//-----------------------------------------------------------//

publicclass Square extends Rectangle {

   public Square(double a) {

       super(a, a);

   }

}

//-----------------------------------------------------------//

//-----------------------------------------------------------//

importjava.io.*;

publicclass MainClass {

   public static void main(String[] args) {

       Figure[] figures = new Figure[3];

       figures[0] = new Circle(10);

       figures[1] = new Rectangle(1, 2);

       figures[2] = new Square(3);

       double sum = 0;

       for(int i = 0; i<figures.length; i++)

           sum += figures[i].getArea();

       try{

           BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

           double d = Double.parseDouble(br.readLine());

           System.out.println(d+sum);

       } catch(IOException exc) {

           System.out.println(«Error!»);

       }

   }

}

//-----------------------------------------------------------//


Проектирование иреализация компилятора.

Для реализациикомпилятора был использован язык программирования Java (JDKверсии 1.5). Это позволяет запускать данный компилятор на любой платформе, длякоторой существует виртуальная машина Java v 1.5.

При каждом запускекомпилятора обрабатывается один файл исходного текста на языке ассемблера дляплатформы Java. Компилятор принимает два аргументакомандной строки: имя файла исходного текста и имя создаваемого файла класса(явное указание расширения .classобязательно). В случае, если выходной файл уже существует, он перезаписываетсябез предупреждения. В случае синтаксической или иной ошибки на консольвыводится соответствующее сообщение.

Можно выделить несколькоосновных этапов компиляции (проходов):

·    Чтение исходногофайла. При этом он разбивается на предложения, разделенные точками с запятыми,также выбрасываются комментарии;

·    Разбор исходноготекста. При последовательном переборе списка предложений выделяютсясинтаксические конструкции. При разборе используется лексический анализатор,разделяющий предложения на лексемы. На основании выделенных синтаксическихконструкций генерируется внутреннее представление программы, имеющее виддревовидной структуры данных, корнем которой является представление класса вцелом, узлами первого уровня — объекты, соответствующие методам, полям,элементам Constant Pool и т. д.;

·    Замена номеровметок соответствующими смещениями в коде методов;

·    Генерациябайт-кода методов как массивов байт;

·    Генерация файлакласса на основании внутреннего представления программы.

Данное деление являетсяусловным и не означает строгой временной последовательности. Третий и четвертыйэтапы, по сути дела, являются частями второго этапа.

Диаграмма пакетов проектаизображена на рис. 1.

/>

Рис. 1.

Диаграмма пакетов

Корневой пакет проектаимеет имя jasm. Он содержит класс MainClass, метод main() которого является точкой входа в программу, иклассы SyntaxErrorException и InternalCompilerErrorException, унаследованные от java.lang.Exception и представляющие ошибки, которые могут возникнуть в процессекомпиляции. Пакет compiler содержитклассы, ответственные за разбор исходного текста, причем классы, работающие скодом методов, содержатся во вложенном пакете code_compiler. Пакет structures содержит классы, из объектов которыхсостоит внутреннее промежуточное представление программы, а также некоторыевспомогательные классы. Он имеет три вложенных пакета: commands, consts и attributes, классы из которых описывают, соответственно, команды байт-кода,элементы Constant Pool и атрибуты полей и методов. В пакет commands в свою очередь вложены пакет command_formats, содержащий базовые абстрактные классы для командтипичных форматов, пакеты, содержащие классы, представляющие команды каждого изтипичных форматов, а также пакет specialдля классов, представляющих команды, имеющие особый формат.

Большинство классов изпакета structures входят в иерархию, корнем которойявляется интерфейс IStructure,содержащий два метода int getLength() и byte[] getContent(), позволяющие получить,соответственно, размер, который займет структура при записи в выходной файл, имассив байт, которыми она представляется в выходном файле. Данный интерфейс неиспользуется для полиморфного вызова методов, он играет лишь рольструктурирования программы. Основные классы пакета structures изображены на диаграмме на рис. 2.

/>

Рис. 2.

Классы пакета jasm.structures.

Генерируемый класс какцелое представляется объектом класса ClassFile, который содержит в своих полях ссылки на объекты классов ConstantPool, FiledInfo и MethodInfo, описывающие область констант, поляи методы создаваемого класса. Сам класс ClassFile интерфейс IStructure не реализует. Среди его членовследует отметить метод writeToFile(), создающий файл класса на основании информации, содержащейся вобъекте.

Данный компилятор можетсоздавать атрибуты методов Code, Exceptions и атрибут поля ConstantValue, которые представляются классами,производными от AttributeStructure.Отмечу, что объект класса CodeAttribute содержит байт-код метода уже в виде массива байт, а не в виде объектовклассов, представляющих отдельные команды (производных от Command), списки таких объектов используютсялишь в процессе обработки кода метода.

Абстрактный класс Command дополнительно к методам интерфейса IStructure содержит абстрактный метод byte getByte(int n), который долженвозвращать байт с заданным номером в команде. Еще один метод changeOffset имеет пустую реализацию, но переопределяетсяв классах-потомках, соответствующих командам перехода. Он используется длязамены номеров меток смещениями на третьем этапе компиляции. Непосредственныминаследниками класса Command являютсяклассы, соответствующие типичным форматам команд (абстрактные) и командам,имеющим уникальные форматы. Большинство команд представляются классами,наследующими классы типичных форматов. Имена классов команд имеют вид C_xxx, где xxx — мнемоническое имя команды. Пустой команде none соответствует класс NoCommand.

Класс ConstantPool содержит как общий список для всехтипов констант, хранящий объекты класса CpInfo (базовый тип для классов, представляющих различныевиды констант), так и списки для констант отдельных типов, содержащие индексыэлементов в первом списке. Эти списки описываются классами, вложенными в класс ConstantPool. Такая структура используется длятого, чтобы при добавлении константы можно было быстро проверить, неприсутствует ли уже идентичный элемент в ConstantPool (эта проверка производится не длявсех типов констант). Для каждого типа констант в классе ConstantPool существует свой метод добавления,который возвращает индекс добавленного (или найденного существующего) элементав общем списке. Среди наследников CpInfo имеется специальный класс CpNone,который соответствует пустой структуре, вставляемой после констант типа Long и Double т. к. следующий за ними индекс считаетсянеиспользуемым.

За процесс компиляцииотвечает пакет compiler, которыйсодержит следующие классы:

·    Source — выполняет функцию выделенияпредложений в исходном тексте. Конструктор этого класса принимает в качествепараметра имя файла, содержимое которого разбивается на предложения и заноситсяв коллекцию типа ArrayList<String>. Метод String nextStatement() при каждом вызове возвращаеточередное предложение;

·    StringParser — выполняет функцию разделения строкна лексемы. Каждый объект этого класса соответствует одной обрабатываемойстроке.

·    TokenRecognizer — имеет статические методы,позволяющие определить, является ли некоторая строка ключевым словом иликорректным идентификатором. Объекты этого класса никогда не создаются;

·    DescriptorCreator — содержит статические методы длясоздания дескрипторов типов, полей и методов из строк, содержащих запись типови сигнатур, используемую в языке. Экземпляры класса также не создаются;

·    ClassHeaderParser, FieldDeclarationParser, MethodHeaderParser — используются при анализе заголовкакласса, описаний полей и заголовков методов. Объекты этих классов сохраняют всебе информацию, извлеченную из анализируемого  в конструкторе классапредложения;

·    code_compiler.CodeCompiler — осуществляет анализ кода метода игенерацию байт-кода (включая третий и четвертый этапы компиляции). Данныйпроцесс будет рассмотрен подробнее ниже;

·    code_compiler.CommandCompiler — анализирует команды в исходномкоде метода и создает объекты классов-потомков Command;

·    code_compiler.Label — представляет метку в коде метода;

·    code_compiler.LabelTable — таблица меток метода. Содержитимена меток, номера соответствующих им строк и смещения команд.

·    SourceAnalyser — занимает центральное место впроцессе анализа исходного текста. Конструктор данного класса принимает вкачестве параметра объект класса Source.При вызове метода analyse()происходит анализ исходного кода и генерируется промежуточное представлениепрограммы в виде описанной выше структуры. В процессе анализа используютсяклассы StringParser, ClassHeaderParser, FieldDeclarationParser, MethodHeaderParser, CodeCompiler и др. Данный методвозвращает объект класса ClassFile.

Класс MainClass содержит единственный метод main, являющийся точкой входа впрограмму. Здесь вначале создается объект класса Source, который передается для обработки объекту класса SourceAnalyser, затем у возвращенного методом SourceAnalyser.analyse() объекта класса ClassFile вызывается метод writeToFile, который и генерирует файл класса,являющийся результатом работы компилятора. Все перечисленные операции заключеныв блок try/catch, перехватывающий любые исключения, в случаевозникновения которых на консоль выводится соответствующее сообщение и процесскомпиляции завершается. Диаграмма, в упрощенном виде показывающая этот процесс,изображена на рис. 3.

/>

Рис. 3.

Обработка исходногофайла.

        

Рассмотрим подробнеепроцесс компиляции кода метода. После обработки заголовка метода с помощьюкласса MethodHeaderParser, в случае, если метод не являетсяабстрактным, в методе SourceAnalyser .analyse()считываются предложения maxstack и maxlocals. Затем считываются и заносятся вмассивы предложения, содержащие команды и описания защищенных блоков. Этимассивы, а также ссылка на объект ConstantPool, представляющий область констант класса, передаются вкачестве параметров конструктору класса CodeCompiler. У созданного объекта CodeCompiler вызывается метод compile(), возвращающий объект класса CodeAttribute, описывающий атрибут Code, содержащий байт-код метода. При этомпроисходят следующие процессы. В конструкторе класса CodeCompiler из строк, содержащих команды,выделяются имена меток, которые сохраняются в объекте класса LabelTable. Затем обрабатывается список строк,описывающих защищенные блоки. В методе CodeCompiler.compile() выполняются следующие операции. Сначала с помощьюобъекта класса CommandCompilerдля каждой команды создается объект соответствующего класса. При этомодновременно для команд, при которых имеется метка, в объекте LabelTable сохраняется информация о смещенииметки относительно начала метода. Как в описаниях защищенных блоков, так и вобъектах, соответствующих командам перехода, на момент окончания этого шагавместо смещений перехода, содержатся порядковые номера команд, при которыхрасположены соответствующие метки. Замена их на действительные смещенияпроизводится на последнем шаге с помощью методов LabelTable.changePC() и Command.changeOffset().


Заключение.

Технология Java ориентирована на использованиеодного языка программирования. Система типов данных и другие особенности языка Java тесно связаны с функционированием JVM и форматом файла класса. Однако,существует открытая спецификация, позволяющие создавать как собственныереализации JVM, так и альтернативные средстваразработки. С ее использованием мною разработан язык JASM, представляющий собой язык ассемблера для платформы Java, который позволяет создавать файлыклассов, использующие значительную часть возможностей JVM, и реализован его компилятор.


Использованнаялитература.

1.        Грис, Д.Конструирование компиляторов для цифровых вычислительных машин. М., «Мир»,1975.

2.        Эккель, Б.Философия JAVA. СПб. 3-е изд.: Питер, 2003.

3.        TimLindholm, Frank Yellin.  The Java Virtual Machine Specification Second Edition.Sun Microsystems Inc. 1999.

еще рефераты
Еще работы по информатике, программированию