Программно переименовывать функции
В настоящее время я пишу компилятор 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Для каждогозависимость потока край посетить целевой узелЕсли этот узел является условным оператором 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 упоминается в комментариях, та же проблема возникает для доступа к свойствам с использованием скобки. ТакНаручники иззапись окружения объекта можно переименовать только вручную.