Пошаговая трансляция



Скачать 104.86 Kb.
Дата25.06.2018
Размер104.86 Kb.
#33523
ТипПрограмма

Языки программирования.

Лекция 16.



Глава 6: Раздельная трансляция
Вопрос тесно связан с понятием физического модуля.
Виды трансляций:

1. Пошаговая трансляция (программа разбивается на естественные куски-строки, которые передаются кусочками в транслятор и сразу же выполняются, если он не мог выполнить, то ждал следующий кусок) - Basic;
2. Инкрементная трансляция (сам компилятор без вмешательства программиста определяет, что за кусочки нужны и без передаёт их транслятору)- современные отладчики, при незначительных изменениях в коде. ;
3. Цельная трансляция (программа целиком поступает на вход транслятору) - любой скриптовый язык ,

компилируемые ЯП: программа из одного языка Л1 переводится в язык Л2(объектный язык), который уже интерпретируется ( язык JVM).

Цельные компилируемые языки (языки, созданные не для индустриального программирования)- Алгол-60(для обмена алгоритмами), стандартный Паскаль(для обучения программирования).
4. Языки с раздельной трансляцией - программа разбивается на модули, возникает понятие физического модуля (ФМ) или единицы компиляции, т.е. при раздельной трансляции используются компилируемые яп.
В компилируемых яп чётко разделяются 2 фазы: трансляция и сборка программы (после которой образуется исполняемый код).
Уже в Фортране была реализована возможность раздельной трансляции (РТ). Все процедуры и ф-ии- единицы компиляции. Но там она была хороша только тем, что она там есть. Она никак не была поддержана средствами защиты.

-->РТ - одно из самых критичных свойств яп, которые ориентированны на индустриальное программирование.


Рассмотрим понятия:

Контекст трансляции(КТ) - совокупность имен и их атрибутов, необходимых для трансляции модуля:

- В контекст входят имена, которые не определяются в самом модуле, но используются в нём;

- Всегда не пуст;

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

для стандартного Паскаля в контекст входят стандартные идентификаторы (их смысл определяется транслятором, но они не входят в стандартные служебные слова)--> в КТ определяют имена стандартного окружения и только они.

Раздельная трансляция:

Из единиц компиляции (ЕК) есть понятие экспорта и всё, что экспортируется, образует контекст трансляции (КТ),

было реализовано уже в Assembler--> C

Очередная ЕК подаётся на вход транслятору, который затем формирует объектный файл. Это присуще любой системе с раздельной трансляцией. Различия в том, что возникает два понятия - трансляционная библиотека(ТБ)

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

Пример:


статическая функция, которая необходима только в этом файле--> необходима в программной библиотеке, но информация о ней не попадает в контекст трансляции.
Простейший вариант раздельной трансляции:

ЕК поступает на вход транслятору, который формирует некоторую часть программной библиотеки (объектный модуль). Откуда в такой схеме берётся контекст трансляции?

Если транслятор видит только ЕК, то весь контекст трансляции должен быть в ЕК. В языке должны быть конструкции, описывающие этот контекст:

В ассемблере - это конструкции:

PUBLIC имя

EXTRN W: WORD - конструкция импорта, находится в другом файле.



В языке Си подобного рода объявления - это ключевое слово extern.
4.1. Языки с такой схемой - языки с раздельной независимой трансляции (компилятор в один момент видит только одну единицу компиляции).

Очевидной недостаток таких языков - каждая ЕК должна заново описывать свой контекст. И если две ЕК используют один контекст, то появляется некоторая избыточность. Если мы перепутаем, укажем неверный спецификатор, то никто никакой ошибки не найдёт - язык объектных файлов не содержит такой возможности.

Максимум- предупреждение.

ЕК0


int a;

ЕК1


extern int a;

ЕК2


extern int a;
И на помощь приходит технология - если мы описываем какой-то контекст, экспортируем, то мы должны интерфейс (всю совокупность экспортированных имён) выносить в специальный header-файл.

Если нужно имя переменной, типа,..., то вместо объявления extern надо целиком включить контекст трансляции - нужный header-файл при помощи #include.


Если надо включить в программу M2.h и M3.h, а М2 содержит в себе объявления из М3, то с точки зрения компилятора Си при объявлении нескольких extern-переменных ничего страшного не возникнет. Но беда может возникнуть если мы объявляем типы:

например, если в М3 содержится описание структуры:

struct c {

}

--> двойное объявление


Выход - проверка на объявление специального символа, например для модуля MyModule.h это может быть __MYMODULE_H__(уникальный для данной единицы трансляции)
И проверка будет иметь вид:

#ifndef __MYMODULE_H__// определен модуль или нет?

#define __MYMODULE_H__// описание, если не было ранее.

...сам модуль MyModule.h....

#endif

И при такой технике гарантируется, что каждое имя описано только один раз.


НО(для С и С++):

1. При изменении одного header-модуля нужно перетранслировать все модули, которые его включают.

2. У нас только единое пространство имён.
Все header-файлы образуют трансляционную библиотеку.

#include

#include

#include // необходимо подключить еще библиотеку


lseek()-достаточно include

Но для использования gethostname() компилятору уже надо подключать специальную библиотеку при помощи следующих команд линковщика:

-l inet

-L build_path // путь к этой библиотеке



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

Язык С++ использует независимую трансляцию, чтобы облегчить распространение этого языка.

4.2. Раздельная зависимая трансляция.
Транслятор когда читает ЕК, то он черпает информацию из нее и из ТБ (трансляционной библиотеки) - и на выходе будет не только ПБ, но и обновление трансляционной библиотеки. Директивы описания контекста ориентированы на имена модулей.

Языки 70-х, 80-х, 90-х гг.

В этой трансляции физический модуль (ФМ) аналогичен логическому модулю (ЛМ).
Такое наблюдается, например, в языке Дельфи.

Юнит(ЕК) в Дельфи транслируется в DFU, который содержит объектные модули и таблицу символов(ПБ и ТБ).


В Модуле-2 есть понятие библиотечного модуля, который делится на definition mudle и implementation module. На уровне исходного текста разделены ПБ и ТБ.
Та же самая идея и в Обероне - у нас существует единая ЕК - модуль. Реально оттранслированный код модулей выглядит как DFU - оттранслированный текст начинается с таблицы символов(все, что помечено *), а далее объектные модули.
Ключевые слова в этих языках:

IMPORT модуль -список имен модулей (компилятор подгружает их)

USES модуль- для delphi;

Но здесь структура всё равно очень проста за счёт того, что ЛМ совпадает с ФМ.


Наиболее мощно это реализовано в языке Ада.

Восходящий подход (bottom-up) - сначала компилируются модули нижнего уровня, потом выше(модули, основанные на предыдущих) и т.д. Сервисные модули ничего не знают о своих клиентах - типичный случай одностороннего связывания.

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

М1 импортирует М2, а М2 импортирует М1 (зацикливание) - разрешение такой ситуации невозможно.



Си и Модула-2 находят эту ошибку на этапе трансляции(препроцессор):

Си : Будет попытка открыть файл на чтение, который уже открыт на запись.

Модула-2: М2 откомпилирован? он может быть откомпилирован только после компиляции М1, который сейчас компилируется.

Решение проблемы - переделка одного или нескольких модулей.


Ключевое отличие языка Ада - он поддерживает и восходящее, и нисходящее программирования(вложенные пакеты).

Там есть понятие пакетов.

Внутри какого-то пакета М могут быть вложенные пакеты М_inner, M1_inner:

package M is

package M_inner is

package M1_inner is

end M1_inner;

end M_inner;

end M;

Соответствующие тела вложены:



pakage body M

body M_inner

body M1_inner

end M;


если мы можем отдельно транслировать пакеты, то мы должны транслировать и вложенные пакеты.

Ситуация со вложенностью отличается зависимостью пакетов, М от М1, т.к. он может использовать имена М1, и М1 зависит от М --> В результате, у нас возникают двусторонние связи.

И Ада поддерживает и односторонние связи.
Основной принцип языка Ада - раздельно транслируемая программа не должна отличаться от цельно транслируемой - чтобы можно было программу транслировать единым куском, а не несколькими единицами компиляции.

можно программу разбивать на:



  • совокупность одноуровневых единиц компиляции( модула-2, оберон, delphi);

  • тела пакетов;

У нас возникает понятие первичных модулей и вторичных модулей.

Первичные модули(спецификация пакета) - это библиотечные модули в таких языках как Модула-2.

Тело пакета - вторичный модуль. Вторичный модуль - раздельно транслируемая процедура или функция.


В первом способе, когда мы разделяем программу на совокупность частей, у нас появляется специальное описание:

WITH список пакетов- подгружает контекст трансляции.

текст ЕК

Здесь единицей контекста является только пакет.

Это один в один соответствует конструкциям import и uses в Обероне и Дельфи соответственно.
WITH M1 is

package M2 is

X:T; // ERROR, т.к. Т видно только потенциально.

X:M1.T; // OK - видимость только через имя соответствующего модуля


Но есть модификация:

WITH список_пакетов;

USE список_пакетов2; // этот список - подмножество первого списка
Это напоминает конструкцию USES из Дельфи - там она подгружает список имен из соответствующего юнита и делает их непосредственно видимыми .

Это аналог конструкции одностороннего связывания модулей типа языков Оберон и Дельфи.


Но поддерживается ещё и двустороннее связывание - можно раздельно транслировать тело и спецификацию пакета М, но и тело пакета M_inner.

для with, нет обратной конструкции, т.к. это одностороннее связывание.

Для этого нам нужна пара конструкций:

Вместо спецификации пакета M_inner, мы пишем:

package M_inner is separate;- говорит компилятору поискать таблицу имен M_inner где-то еще.

Это в одну сторону, а в другую сторону -

package M is stub

...


package M_inner is separate // stub - заглушка, при трансляции увидев заглушку, компилятор полезет в ТБ и загрузить таблицу имен для М_inner, не зная таблицы имен транслировать дальше нельзя

...


end M;
Что будет, если мы хотим оттранслировать отдельно тело пакета M_inner:

package body M_inner is

end M_inner

нельзя, т.к. М_inner внутри пакета М


separate (M) package body M_inner is

...


end M_inner;-двустороннее связывание

(М)-имя объемлющего пакета.


Когда компилятор транслирует, какую таблицу имён он должен подключить в этой точке (точке, где стоит (М))? Рассмотрим 2 возможности - эту и ещё:

WITH M;


package MM is

...


end MM; - это одностороннее связывание, надо подключить все имена из спецификации пакета М.
Для двустороннего связывания где-то должна стоять заглушка (в теле пакета М):

package body M_inner is separate

К моменту точки, где (М) должны быть оттранслированы все имена пакета М , совокупность имён из начала пакета М (до заглушки).

Mы вынесли М_inner и физически оно находится вне пакета М(транслируется как отдельная единица), но мы хотим, чтобы сохранялась вложенность этого пакета в М(доступ к именам). Т.о. тело пакета М должно быть оттранслировано раньше.

Это существенно усложняет процесс трансляции.

Сохранить всю таблицу имён - сохранить то, что было объявлено в М до заглушки M_inner.

Заглушки говорят, что в этой точке надо сохранить всю таблицу имен, потом при трансляции в этой точке она понадобится.

Двустороннее связывание есть только в языке Ада при помощи существенного усложнения механизма раздельной трансляции. Еще одной проблемой ,усложняющей структуру,является раздельная трансляция родовых пакетов.


Механизм раздельной трансляции хорошо ложится на модульную структуру.

Модуль, как физическая единица и как логическая единица, хорошо коллерируют между собой.


А что в языках с классовой структурой? Они пошли другим путём.
В языках медленно но верно исчезают различия между ТБ и ПБ.

В Дельфи, C# и Java оттранслированный код - отнюдь не старый объектный код...

Java:

файл с расширением .class, в котором:

- объектный код на языке VJM;

- вся информация об именах (публичные и защищенные)

Обмануть компилятор Java очень тяжело обмануть, в отличие от компилятора C++ можно получить доступ к приватным членам, посредством описания структуры класса, эквивалентного данному по распределению памяти, у которого все имена- публичные, и преобразовав указатель на один класс в указатель на другой, у которого все приватное.

В Java неконтролируемое преобразование запрещено--> взлом на уровне языка запрещен.


В современных яп для обеспечения безопасности вся содержательная информация об именах храниться в неоттранслированном коде. Существуют специальные программы, которые могут восстановить по соответствующему коду восстановить исходный текст, за исключением имен локальных переменных.(disassembly), комментариев, кроме тех, которые вставляются в объектный код:

/// - В C#

//* - В Java
В современных яп сливаются программная и трансляционная часть.

В C# и Java единица управления контекстом - это пространство имён (namespace) и пакет (package).

Java (используемые имена):

import package_name.class_name;

import package_name.*;- все классы

если нет конфликта имен, то соответствующие имена становятся видимыми непосредственно.


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

package_name.class_name

То есть, обязательного требования импортирования нет.

import- для ускорения процесса трансляции. В этот момент компилятор находит соответствующий пакет, в нем- соответствующий класс. Загружает таблицу имен . В классе содержится и объектный код, и таблица имен, т.к. нет разницы между ТБ и ПБ. Не используя импортирование каждый раз, когда встречается обращение проделывается та же операция, следовательно при частом обращении импортирование экономит время.

Аналогично и в C#:

using namespace_name;-все классы

using System;- все имена из модуля System видим непосредственно.

System.Console-напрямую.



Способ зависит от частоты использования.




Скачать 104.86 Kb.

Поделитесь с Вашими друзьями:




База данных защищена авторским правом ©vossta.ru 2023
обратиться к администрации

    Главная страница