Углубленный анализ принципов компиляции Javac

Java задняя часть JVM переводчик

что такое джавак

Обычно файл java компилируется в файл байт-кода .class компилятором, а затем транслируется в исполняемый файл на компьютере виртуальной машиной java JVM.

Известный нам язык java имеет свою собственную спецификацию синтаксиса, и та же самая JVM также имеет свою собственную спецификацию синтаксиса.Как адаптировать синтаксические правила java к правилам разбора синтаксиса - это роль javac.Короче говоря, роль javac заключается в том, чтобы Исходный код Java преобразуется в файл байт-кода класса.

Базовая структура компилятора Javac

Шаг компиляции

image

1. Лексический анализатор:

1.1 Функция:

Преобразование исходного кода в поток токенов

1.2 Процесс

Прочтите исходный код, начните с символа в исходном файле, узнайте пакет, импорт, определение класса, атрибут, определение метода и т. д., в свою очередь, в соответствии со спецификацией синтаксиса java и, наконец, постройте абстрактное синтаксическое дерево.

1.3 Примеры

package compile;

/**
 * 词法解析器
 */
 public class Cifa{
     int a;
     int c = a + 1;
 }

Преобразование в поток токенов:

image

1.4 Анализ исходного кода

  • com.sun.tools.javac.parser.JavacParser   указывает, какие слова соответствуют спецификации языка Java, а конкретные операции чтения и классификации различных лексических выражений выполняются сканером.
  • com.sun.tools.javac.parser.Scanner   отвечает за чтение отдельных символов исходного кода один за другим, а затем синтаксический анализ последовательности Token, соответствующей спецификации языка Java, и вызов nextToken() один раз создаст Token
  • com.sun.tools.javac.parser.Tokens$TokenKind   содержит все типы токенов, такие как BOOLEAN, BREAK, BYTE, CASE.
  • com.sun.tools.javac.util.Names   используется для хранения и представления разобранной лексики, каждый набор символов будет объектом Name, все объекты хранятся во внутреннем классе Name.Table.
  • com.sun.tools.javac.parser.KeyWords   отвечает за сопоставление набора символов с набором токенов, например, package zxy.demo.com; Token.PACKAGE = package, Token.IDENTIFIER = zxy.demo.com, ( эта часть снова разделяется на чтение первой лексемы, которая является zxy, чтобы определить, является ли следующая лексема «.», если это так, то читаем следующую лексему типа Token.IDENTIFIER, повторяем до тех пор, пока следующая лексема не будет «.». , то есть читается следующая лексема A не типа Token.IDENIFIER, Token.SEMI =; то есть читается Name лексемы типа TIDENTIFIER), и за эту задачу отвечает класс KeyWords.

1.5 Вопросы

Как Javac различает каждый токен? Например, как это работает, пока пакет не является ключевым словом, а не пользовательской переменной?

Когда Javac проанализирует этот метод, JavacParser будет контролировать порядок в соответствии со спецификацией языка Java и то, какой токен появится на месте.Например, пакет может стоять только в самом начале файла.

Как Javaac определяет, какие символы объединяются для формирования токена? Как он отделяет токен от строки потоков символов?

Для ключевых слов это в основном определяется правилами грамматики ключевых слов, например, пакет является ключевым словом, если пакет строки является непрерывным.

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

Пример:

int a = 1 + 2;

начать с пакета

.....

int - это TOKEN:INT, определяемый грамматическими ключевыми словами

разделенные пробелами между in a

a — это пользовательская переменная, которая определена как TOKEN:IDENTIFIER.

a = отделено пробелом (некоторые друзья в это время скажут,int a=b+c; Это предложение правильное., да, в большинстве случаев такое разделение без пробелов действительно может скомпилироваться,Это связано с тем, что Java указывает, что при объявлении переменной она должна начинаться с буквы, знака подчеркивания или знака доллара.Когда JavacParser читает a для чтения =, это не будет переменной до тех пор, пока =) определит = как TOKEN:EQ

1 оценивается как TOKEN:INTLITERAL

.....

Идентифицировать ; как ЖЕЛЕЗ:SEMI

.....

Наконец, читается конец класса, то есть } определяется как TOKEN:RBRACE

2. Синтаксический анализатор:

Только что лексер проанализировал исходный файл Java в поток токена.

Теперь синтаксическому анализатору необходимо скомпоновать поток токенов в более структурированное синтаксическое дерево. То есть поместить слова в этих потоках токенов в предложение, полное предложение.

2.1 Функция

Токены в потоке Токенов, сформированном после лексического анализа, формируются в предложение, и предложение проверяется на соответствие спецификации языка Java.

2.2 Три части синтаксического анализа

  • package
  • import
  • Класс (включая класс, интерфейс, перечисление), класс, упомянутый ниже, обычно относится к этим трем категориям, а не только к классу.

2.3 Используемая библиотека классов

  • com.sun.tools.javac.tree.TreeMaker   Все синтаксические узлы генерируются им, а синтаксический узел строится на основе объекта Name
  • com.sun.tools.javac.tree.JCTree$JCIf    Все узлы наследуют jctree и реализуют **tree, например JCIf extends JCTree.JCStatement реализует IfTree
  • Три свойства com.sun.tools.javac.tree.JCTree
    • Tree tag:每个语法节点都会以整数的形式表示,下一个节点在上一个节点上加1;
      
    • pos:也是一个整数,它存储的是这个语法节点在源代码中的起始位置,一个文件的位置是0,而-1表示不存在
      
    • type:它代表的是这个节点是什么java类型,如int,float,还是string等
      

2.4 Примеры

package compile;

/**
 * 语法
 */
public class Yufa {
    int a;
    private int c = a + 1;
    
    //getter
    public int getC() {
        return c;
    }
    //setter
    public void setC(int c) {
        this.c = c;
    }
}

image

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

Примечание 1: Если в классе есть ключевое слово импорта, на пути есть узел синтаксиса импорта.

Примечание 2. Генерация всех синтаксических узлов выполняется в классе TreeMaker.

3. Семантический анализатор

3.1 Функция

Преобразуйте синтаксическое дерево в синтаксическое дерево аннотаций, то есть выполните некоторую обработку этого синтаксического дерева.

3.2 Шаги

  • Добавьте в класс конструктор по умолчанию (выполняется классом com.sun.tools.javac.comp.Enter)

  • Обработка аннотаций (выполняется классом com.sun.tools.javac.processing.JavacProcessingEnvironment)

  • Проверить правильность семантики и сделать логические выводы (завершено com.sun.tools.javac.comp.Attr)

    • соответствует ли тип переменной
    • Инициализируется ли переменная перед использованием
    • Возможность выводить типы параметров для универсальных методов
    • Слияние строковых констант
  • Анализ потока данных (выполняется классом com.sun.tools.javac.comp.Flow)

    • Убедитесь, что переменная назначена правильно (например, метод с возвращаемым значением должен обязательно иметь возвращаемое значение)
    • Убедитесь, что конечные переменные не будут повторно изменены
    • Определить тип возвращаемого значения метода
    • все ли проверенные исключения выбрасываются или перехватываются
    • Все операторы выполняются (операторы после return не будут выполняться, кроме блока finally)
  • Семантический анализ синтаксических деревьев (выполняется com.sun.tools.javac.comp.Flow)

    • Удалите бесполезный код, например блокировку if только с постоянным значением false
    • Автоматическое преобразование переменных, например автоматическое преобразование int в тип Integer.
    • Удалите синтаксический сахар и преобразуйте форму foreach в более простой цикл for.

Наконец, создается синтаксическое дерево аннотаций.

3.3 Используемая библиотека классов

  • com.sun.tools.javac.comp.Check, который используется, чтобы помочь классу Attr проверить правильность типа переменной в синтаксическом дереве, например, соответствует ли возвращаемое значение метода полученному ссылочному типу значения.
  • com.sun.tools.javac.comp.Resolve, используется для проверки того, является ли доступ к переменной, методу или классу законным и является ли переменная статической переменной.
  • com.sun.tools.javac.comp.ConstFold, который объединяет несколько символов строковой константы в одну строку.
  • com.sun.tools.javac.comp.Infer, чтобы помочь вывести типы параметров для универсальных методов.

3.4 Примеры

Автоматическое преобразование переменной

public class Yuyi{
    public static void main(String agrs[]){
        Integer i = 1;
        Long l = i + 2L;
        System.out.println(l);
    }
}
//经过自动转换后
public class Yuyi{
    public Yuyi(){
        super();
    }
    public static void main(String agrs[]){
        Integer i = Integer.valueOf(1);
        Long l = Long.valueOf(i.intValue() + 2L);
        System.out.println(l);
    }
}

удалить синтаксический сахар

public class Yuyi{
    public static void main(String agrs[]){
        int[] array = {1,2,3};
        for (int i : array){
            System.out.println(i);
        }
    }
}
//解除语法糖后
public class Yuyi{
    public Yuyi(){
        super();
    }
    public static void main(String agrs[]){
        int[] arrays = {1,2,3};
        for (int[] arr$ = array,len$=arr$.length,i$=0; i$<len$; ++i$){
            int i = arr$[i$];
            {
                System.out.println(i);
            }
        }
    }
}

Разрешение внутреннего класса

public class Yuyi{
    public static void main(String agrs[]){
        Inner inner = new Inner();
        inner.print();
    }
    class Inner{
        public void print(){
            System.out.println("Yuyi$Inner.print");
        }
    }
}
//转化后的代码如下
public class Yuyi{
    public Yuyi(){
        super();
    }
    public static void main(String agrs[]){
        Yuyi$Inner inner = new Yuyi$Inner(this);
        inner.print();
    }
    {
    }
}
class Yuyi$Inner{
    /*synthetic*/ final Yuyi this$0;
    
    Yuyi$Inner(/*synthetic*/final Yuyi this$0){
        this.this$0 = this$0;
        super();
    }
    
    public void print(){
        System.out.println("Yuyi$Inner.print");
    }
}

4. Генератор кода

4.1 Функция

После создания синтаксического дерева Javac вызовет класс com.sun.tools.javac.jvm.Gen для обхода синтаксического дерева и создания байт-кода Java.

4.2 Шаги

  • Преобразуйте блок кода в методе java в командную форму, соответствующую синтаксису JVM.Операции JVM основаны на стеке, и все операции должны выполняться путем извлечения и перемещения стека.
  • Вывод байт-кода в файл с расширением класса в соответствии с форматом файловой организации JVM.

4.3 Используемая библиотека классов

  • Класс com.sun.tools.javac.jvm.Gen используется для обхода синтаксического дерева и генерации окончательного байт-кода Java.
  • com.sun.tools.javac.jvm.Items, вспомогательный генератор, этот класс представляет любой адресуемый элемент операции, который может отображаться в стеке операций как единое целое.
  • com.sun.tools.javac.jvm.Code, вспомогательный генератор, хранит сгенерированный байт-код и предоставляет некоторые методы, способные отображать коды операций.

Справочник: "Углубленный анализ Java Web"