Премахване на код за отстраняване на грешки от вътрешността на функция с помощта на прости оптимизации на Closure Compiler

Търся начин да премахна кода за отстраняване на грешки от функциите, за да мога да добавя тестови куки към затваряния. Прочетох Google Closure Compiler Advanced: премахване на кодови блокове по време на компилиране и тествано премахване на код за отстраняване на грешки със следното:

/** @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 дефиниции от файловете и да ги инжектирам в източника или extern преди изпълнение, но се натъкнах на два проблема:

  • Дефинирането му във extern изглежда не прави нищо.
  • Undefined 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}

DEBUG = вярно;

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

--define="DEBUG=false"

person user2712511    schedule 27.01.2014

Персонализирана компилация на компилатора ще ви позволи да направите това. По принцип искате да "вградите постоянни променливи":

options.setInlineConstantVars(true);

Можете да го добавите тук, в 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 може да види, че не можем да направим нещо като 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 Library, за да предостави опции и флагове за компилатора.

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