Удаление кода отладки изнутри функции с помощью простых оптимизаций компилятора Closure

Я ищу способ убрать отладочный код из функций, чтобы добавить тестовые хуки к закрытию. Я прочитал Расширенный компилятор Google Closure: удаление блоков кода во время компиляции и протестировал удаление отладочного кода следующим образом:

/** @define {boolean} */
var DEBUG = true;

if (DEBUG) {
    console.log('remove me');
}

Простая оптимизация с --define='DEBUG=false' сокращает это до var DEBUG=!1;. То же самое относится и к этому:

/** @const */
var DEBUG = false;

if (DEBUG) {
    console.log('remove me');
}

Когда у меня возникают проблемы, я использую это соглашение внутри функции:

/** @const */
var DEBUG = false;

function logMe() {
    if (DEBUG) {
        console.log('remove me');
    }
}

Это сводится к следующему:

var DEBUG=!1;function logMe(){DEBUG&&console.log("remove me")};

Я бы ожидал, что он уменьшится до:

var DEBUG=!1;function logMe(){};

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

Обновить

Согласно ответу @John, я реализовал свой собственный компилятор и обнаружил, что следующая конфигурация удалит if (DEBUG) {} изнутри и снаружи кода для случая @define:

CompilerOptions options = new CompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
//options.setInlineConstantVars(true);
options.setInlineVariables(CompilerOptions.Reach.ALL);
options.setDefineToBooleanLiteral("DEBUG", false);

Это работает достаточно хорошо для одного файла со следующими ограничениями:

  1. Для этого требуется, чтобы var DEBUG был определен в каждом файле, что является плохой практикой.
  2. При объединении нескольких файлов у вас может быть только один var DEBUG, иначе компилятор не сможет оптимизировать его. Этого можно было избежать, скомпилировав каждый файл по отдельности и объединив их.
  3. Поскольку значение определяется в начале файла, нет возможности получить значение заранее.

Я играл с идеей удалить все var DEBUG определения из файлов и вставить их в источник или внешний вид перед выполнением, но я столкнулся с двумя проблемами:

  • Кажется, что определение его во внешнем виде ничего не дает.
  • Неопределенный DEBUG в некомпилированном коде вызывает ошибку ссылки в браузере.

Идеальным вариантом было бы тестирование window.DEBUG, которое не вызывает эталонной ошибки. К сожалению, хотя внедрение /** @const */ var window = {}; /** @const */ window.DEBUG = false; работает на верхнем уровне, уменьшая if (window.DEBUG) {}, оптимизация фактически отменяется, если она помещена в функцию.

Если другой вариант компилятора не работает, единственный вариант, который действительно имеет смысл, - это использовать window.DEBUG и перед компиляцией ввести /** @const */ var DEBUG = false; и глобальную замену /\bwindow.DEBUG\b/ на DEBUG. Есть ли способ лучше?


person Brian Nickel♦    schedule 09.07.2012    source источник
comment
Простые оптимизации Closure Compiler не удаляют неиспользуемый код, а просто минимизируют его. Он выполняет минификацию правильно - компилятор не знает, что эта переменная является неизменяемым флагом. Вам нужно будет использовать расширенную оптимизацию, если вы хотите удалить неиспользуемый код.   -  person rekamoyka    schedule 10.07.2012
comment
Привет, @AlecAnanian, согласно первым двум примерам, простая оптимизация удалит блоки всегда ложных if на уровне скрипта. Я пытаюсь понять, почему этот подход не работает на один уровень ниже в функции.   -  person Brian Nickel♦    schedule 10.07.2012
comment
О, понятно - похоже, вам нужно использовать специальный флаг goog.DEBUG для простой оптимизации, чтобы учесть его.   -  person rekamoyka    schedule 10.07.2012


Ответы (6)


Используйте аннотацию @define:

@define {boolean}

ОТЛАДКА = истина;

И скомпилировать с опцией

--define = "ОТЛАДКА = ложь"

person user2712511    schedule 27.01.2014

Пользовательская сборка компилятора позволит вам это сделать. Вы в основном хотите «встроить постоянные переменные»:

options.setInlineConstantVars (истина);

Вы можете добавить его здесь, в applySafeCompilationOptions: http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/CompilationLevel.java?r=706 < / а>

Или вы можете использовать Java API и добавить опцию (без изменения кода компилятора). Майкл Болин привел здесь пример того, как это сделать:

http://blog.bolinfest.com/2009/11/calling-closure-compiler-from-java.html

person John    schedule 09.07.2012
comment
Прохладный. Завтра мне придется попробовать API-часть. В конце концов, я захочу переложить эту работу на плагин maven, чтобы было разумно написать собственный исполнитель компилятора. - person Brian Nickel♦; 10.07.2012
comment
Я обновил выше. Похоже, options.setInlineVariables(CompilerOptions.Reach.ALL) требуется. К сожалению, есть много предостережений, и я могу думать только о действительно хакерском решении, чтобы заставить его работать. - person Brian Nickel♦; 12.07.2012

Это старый ответ, но я нашел способ, о котором здесь не упоминается.

(function(){
    var DEBUG = true;

    if (DEBUG) {
        if (something === "wrong") {
            console.warn("Stop!  Hammer time!");
        }
        else if (something === "as expected") {
            console.log("All good :-)");
        }
    }

    foo();
})();

С ADVANCED_OPTIMIZATIONS это компилируется в следующее:

"wrong" === something ? 
    console.warn("Stop!  Hammer time!") : 
    "as expected" === something && console.log("All good :-)");
foo();

В нашем сценарии сборки мы можем переписать строку DEBUG, установив для нее значение false, что затем даст этот результат.

foo();

Причина, по которой это происходит, заключается в том, что Closure удалит недоступный код. Создав замыкание и определив локальную переменную, Closure может увидеть, что мы не можем сделать что-то вроде window.DEBUG === true, поэтому код гарантированно никогда не будет запущен.

person Brigand    schedule 30.08.2013

Ваша переменная DEBUG в настоящее время является глобальной. GCC не будет удалять или переименовывать глобальные переменные в простом режиме оптимизации, поэтому они останутся доступными для любого кода в других скриптах, который может захотеть получить к ним доступ. Попробуйте включить свой код в анонимную функцию.

person Oleg V. Volkov    schedule 09.07.2012

Я решил проблему «удаления функций отладки из закрытого скомпилированного javascript с использованием SIMPLE_OPTIMIZATION», объединив аналогичный метод, предложенный @John, а также используя некоторые обновления @Brian Nichols. Я мог только заставить компилятор удалить строки, поместив это в глобальную область моего основного файла js и выполнив настраиваемую компиляцию (используя несколько файлов .js, которые все равно удалили их)

/** @const 
*   @type {boolean}
*/
var DEBUG = false;

//and used this format for my debug function
DEBUG && myLog('foo'); 

а затем компилируем java-компилятор замыкания с помощью ant, чтобы включить эту опцию options.setInlineVariables (CompilerOptions.Reach.ALL); под функцией applySafeCompilationOptions в файле CompilationLevel.java, как предлагает @john. Это сработало для меня и не нарушило мою кодовую базу, как это сделал ADVANCED ...

person steveinatorx    schedule 12.10.2013

Удалите var DEBUG = true; из кода и преобразуйте все условия, которые проверяют if (DEBUG), в if (goog.DEBUG). Измените параметр компилятора на --define goog.DEBUG=false. Переменная goog встроена в API библиотеки Closure для предоставления параметров и флагов для компилятора.

person rekamoyka    schedule 09.07.2012
comment
Я попробовал и получил предупреждение: WARNING - unknown @define variable goog.DEBUG. Код не был удален. - person Brian Nickel♦; 10.07.2012
comment
Для записи я выполнил java -jar compiler.jar --js=test.js -D goog.DEBUG=false с compiler.jar версии 20120430. - person Brian Nickel♦; 10.07.2012
comment
Для этого вам потребуется включить библиотеку закрытия. Если вы его не используете, вам подойдут расширенные настройки оптимизации для компилятора. - person rekamoyka; 10.07.2012
comment
Я прошел через этот процесс, и он работает, но я действительно хотел избежать дополнительных оптимизаций и добавления новой библиотеки. Это действительно высокая цена за подавление блока кода. - person Brian Nickel♦; 10.07.2012
comment
Тем не менее, действительно здорово, как конструктор и компилятор могут взять огромную библиотеку и переработать ее до нескольких компонентов, необходимых для выполнения. - person Brian Nickel♦; 10.07.2012
comment
Хм ... Похоже, goog.DEBUG тоже не волшебство. Он работает только благодаря Closure Library, поэтому нет необходимости включать его для этой задачи. - person Brian Nickel♦; 10.07.2012