Программно переименовывать функции

В настоящее время я пишу компилятор ECMAScipt5, который выполняет различные заданные оптимизации / преобразования в дереве разбора и компилирует обратно в ECMAScipt5.

Одна функциональность заключается в переименованиипереплет вEnvironmentRecord.

Это преобразование может быть выполнено автоматически, например, как часть оптимизации, которая направлена ​​на уменьшение размера кода, где каждой переменной (не в глобальной области видимости) будет присвоено следующее самое короткое доступное имя, или вручную с помощью аннотации после оператора, который вводит новую область.

Однако я должен ограничить (автоматический) процесс только объявлениями переменных.

Рассмотрим эти два примера. Первый, скомпилированный, указав[Minify] в качестве преобразований, второй использует[Directives, PrettyPrint]

Синтаксис:Compiler.fromSource (src).compile ([/*Array of transformations*/]);

var bar,foo;
(function exampleMin () {
    var bar="foo",
        foo="bar";

    function fooBar () {
        return foo + bar;
    }
})

компилируется в

var bar,foo;function exampleMin(){var A="foo",B="bar";function fooBar(){return B+A}}

А также

var bar,foo;
(function exampleMin () {
    @Rename bar:A
    @Rename foo:B
    @Rename fooBar:C
    var bar="foo",
        foo="bar";

    function fooBar () {
        return foo + bar;
    }
})

компилируется в

var bar,foo;
function exampleMin(){
     var A="foo",B="bar";
     function C(){
          return B+A;
     }
};

Что приводит к проблемной части, функции ... рассмотреть следующее

if (fooBar.name === 'fooBar') {
 //...
}

Теперь, если это утверждение будет содержаться вexampleMin, Определяемое пользователем переименование превратило бы код в семантически другой код. Что не должно происходить при автоматическом преобразовании.

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

Это подводит меня к вопросам:

Что, помимо доступа к имени функции, следует учитывать при переименовании функции?Какой анализ необходимо выполнить, чтобы пометить функцию как безопасно оптимизируемую или нет. Это вообще возможно.Я бы предпочел исключить функции из переименования или я бы попытался изменить другую сторону, например, Сравнение с именем функции тоже. (Если это может быть доказано, что у него нет побочных эффектов)Будет ли изменение в семантике терпимым в таком конкретном случае(НКУ кажется так и думает), если я, в обмен, предлагаю@do-not-optimize аннотаций?

Обновление 1.

Я пришел к выводу, что этот анализ может быть невозможен только через статический анализ

Рассмотрим следующий код.

function foo () {}
function bar () {}
var fns = [bar,foo];

if (fns [0].name === 'bar') fns [0] ();

fns.unshift (foo);

if (fns [1].name === 'bar') fns [1] ();

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

Update2

В то же время и после прочтения ответа @Genes я понял, что есть несколько других вещей, которые могут не помешать добавить. Во-первых, некоторые примечания:

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

В настоящее время я работаю над SSA[2] преобразование. Поэтому я еще не реализовал анализ DataFlow. Но это по плану.

Итак, для простоты, давайте просто предположим, что следующие условия будут выполнены.

AST и CFG находятся в форме статического одиночного назначения.Наборы GEN и KILL были вычислены для каждого узла в CFG4Достижение определений4 / IN и OUT наборы были вычислены.DEF / USE пары были вычислены

ребра зависимости потока были добавлены в CFG

Таким образом, график (-ы) потока управления для первого примера может выглядеть примерно так.

Черные пунктирные линии обозначают границы потока управления.Черные пунктирные соединения представляют зависимости потока данныхСиние двойные стрелки показывают места звонков.Синие пунктирные линии представляют межпроцедурные зависимости.Я, однако, не уверен, должен ли я установить прямое соединение между соответствующими узлами каждого предыстории CFG

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

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

Посетите его объявления узла CFGДля каждогозависимость потока край посетить целевой узелЕсли этот узел является условным оператором goto, а ссылкой на функции является LHSсобственность собственности с RHS, являющимся "именем".Отметить функцию как испорченную

Единственная проблемаЯ не вижу, как вычислить (даже приблизить) эту информацию для нестатических ссылок на функцию.

Так, если этот анализ не помогает найтиВСЕ ссылки на функцию, я мог бы также использовать вышеупомянутый список ссылок, что каждыйпереплет взапись среды держит. Поскольку функция имеетдекларативная запись среды так же как запись окружения объекта, Я мог бы просто взглянуть на количество ссылок егообъектная среда "название"переплет.

В качестве ссылки, вот фактический код, который в настоящее время выполняет переименование

var boundIdentifiers = this.environment.record.bindings, //`this` refers to an AST node representing a FunctionDeclaration or a FunctionExpression
    nextName, 
    identifier, 
    binding;

for (identifier in boundIdentifiers) {
    binding = boundIdentifiers [identifier];
    if (binding.uses < 2 && !binding.FunctionExpression) {
        compiler.pushWarning (binding.references [0].line, binding.references [0].column,'Declared function ' + identifier + ' is never called.') //False positive if the functions reference is obtained dynamically
    }

    if (boundIdentifiers [identifier].FunctionDeclaration || boundIdentifiers [identifier].FunctionExpression) {
        continue; //Skip function declarations and expressions, since their name property could be accessed
    }

    do {
        nextName = nextVar (); 
    } while (
        Object.hasOwnProperty.call (boundIdentifiers,nextVar) //There could exist a `hasOwnProperty` binding.
    ); //ther could a with the name that already exists in the scope. So make sure we have assign a free name.

    this.environment.record.setBindingName (identifier, nextName);
}

Таким образом, общая проблема сводится к отлову нестатических ссылок

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

Я изменил вопросы, чтобы соответствовать обновлению. Таким образом, вышеуказанные вопросы все еще применяются

[1]Обзор методов статического анализа программ (СН: 2) [2]Статическая единичная книга назначений [4]Представление и анализ программного обеспечения

Как@didierc упоминается в комментариях, та же проблема возникает для доступа к свойствам с использованием скобки. ТакНаручники иззапись окружения объекта можно переименовать только вручную.

Ответы на вопрос(3)

Ваш ответ на вопрос