Смекни!
smekni.com

Ассемблер для платформы Java (стр. 5 из 6)

end;

public getArea():double;

maxstack 4;

maxlocals 1;

aload_0;

getfield @::m_a:double;

aload_0;

getfield @::m_b:double;

dmul;

dreturn;

end;

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

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

%файл Square.jsm

public class Square;

extends Rectangle;

methods;

public <init>(double):void;

maxstack 5;

maxlocals 3;

aload_0;

dload_1;

dload_1;

invokespecial Rectangle::<init>(double, double):void;

return;

end;

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

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

%файл MainClass.jsm

public class MainClass;

methods;

public <init>():void;

maxstack 1;

maxlocals 1;

aload_0;

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

return;

end;

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

maxstack 8;

maxlocals 7;

iconst_3;

anewarray Figure;

astore_1;

aload_1;

iconst_0;

new Circle;

dup;

ldc2_w double 10;

invokespecial Circle::<init>(double):void;

aastore;

aload_1;

iconst_1;

new Rectangle;

dup;

dconst_1;

ldc2_w double 2;

invokespecial Rectangle::<init>(double, double):void;

aastore;

aload_1;

iconst_2;

new Square;

dup;

ldc2_w double 3;

invokespecial Square::<init>(double):void;

aastore;

dconst_0;

dstore_2;

iconst_0;

istore 4;

l_50:

iload 4;

aload_1;

arraylength;

if_icmpge l_75;

dload_2;

aload_1;

iload 4;

aaload;

invokeinterface Figure::getArea():double, 1;

dadd;

dstore_2;

iinc 4, 1;

goto l_50;

l_75:

new java.io.BufferedReader;

dup;

new java.io.InputStreamReader;

dup;

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

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

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

astore 4;

l_50:

aload 4;

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

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

dstore 5;

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

dload 5;

dload_2;

dadd;

invokevirtual java.io.PrintStream::println(double):void;

l_114:

goto l_127;

l_117:

astore 4;

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

ldc string "Error";

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

l_127:

return;

protected_blocks;

java.io.IOException l_75 : l_114 > l_117;

end;

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

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

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

public interface Figure {

double getArea();

}

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

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

public class 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;

}

}

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

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

public class 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;

}

}

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

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

public class Square extends Rectangle {

public Square(double a) {

super(a, a);

}

}

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

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

import java.io.*;

public class 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). Это позволяет запускать данный компилятор на любой платформе, для которой существует виртуальная машина Javav 1.5.

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

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

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

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

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

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

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

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

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

Рис. 1.

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

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

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

Рис. 2.

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

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

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

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

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

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

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

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

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