9

I'm looking for a way to strip out debug code from functions so I can add test hooks to closures. I've read Google Closure Compiler advanced: remove code blocks at compile time and tested out removing debug code with the following:

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

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

Simple optimisation with --define='DEBUG=false' reduces this to var DEBUG=!1;. The same applies for this:

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

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

Where I run into trouble is using this convention inside a function:

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

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

This reduces to the following:

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

I would expect it to reduce further to:

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

Is there a reason this is not working as expected? I'm really just looking for a clean way to strip debug code and am not ready to take the plunge into advanced optimizations.

Update

Per @John's answer, I implemented my own compiler and have found that the following configuration will remove if (DEBUG) {} from inside and outside the code for the case of a @define:

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

This works well enough for a single file with the following limitations:

  1. This requires var DEBUG to be defined in each file, which is bad practice.
  2. When combining multiple files, you can only have a single var DEBUG or the compiler can't optimize around it. This could be avoided by compiling each file individually and merging them.
  3. Because the value is defined at the beginning of the file, there's no flexibility to receive the value beforehand.

I've toyed with the idea of removing all var DEBUG definitions from the files and injecting it into the source or extern before execution, but I've run into two issues:

  • Defining it in extern appears to do nothing.
  • Undefined DEBUG in the uncompiled code throws a reference error in the browser.

The ideal option would be to test window.DEBUG, which does not throw a reference error. Unfortunately, while injecting /** @const */ var window = {}; /** @const */ window.DEBUG = false; works at the top level, reducing if (window.DEBUG) {}, the optimization is actually reverted if placed in a function.

Unless another compiler option works the only option that would really make sense is to go with window.DEBUG and before compilation inject /** @const */ var DEBUG = false; and to a global replace of /\bwindow.DEBUG\b/ with DEBUG. Is there a better way?

4

6 回答 6

4

使用@define 注解:

@define {boolean}

调试=真;

并使用选项编译

--define="调试=假"

于 2014-01-27T10:58:09.077 回答
3

编译器的自定义构建将允许您执行此操作。您基本上想要“内联常量变量”:

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 并添加选项(无需修改编译器的代码)。Michael Bolin 在这里举了一个例子来说明如何做到这一点:

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

于 2012-07-09T23:33:35.243 回答
2

这是一个旧答案,但我找到了一种此处未提及的方法。

(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,所以代码保证永远不会运行。

于 2013-08-30T11:18:03.907 回答
1

我解决“使用 SIMPLE_OPTIMIZATION 从闭包编译的 javascript 中删除调试函数”问题的方法是结合@John 提出的类似方法以及使用@Brian Nichols 的一些更新。我只能通过将这是我的主 js 文件的全局范围并进行自定义编译(使用多个 .js 文件仍然删除它们)来让编译器删除这些行

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

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

然后用 ant 编译闭包编译器 java 以包含此选项 options.setInlineVariables(CompilerOptions.Reach.ALL); 正如@john 建议的那样,在 CompilationLevel.java 文件中的 applySafeCompilationOptions 函数下。这对我有用,并没有像 ADVANCED 那样破坏我的代码库......

于 2013-10-12T02:10:52.520 回答
1

您的DEBUG变量当前是全局变量。GCC 不会在简单优化模式下删除或重命名全局变量,因此它们仍可用于其他脚本中可能想要访问它们的任何代码。尝试将您的代码封装到匿名函数中。

于 2012-07-09T22:59:40.880 回答
0

从您的代码中删除var DEBUG = true;并将所有检查的条件转换if (DEBUG)if (goog.DEBUG). 修改您的编译器选项以读取--define goog.DEBUG=false. 该goog变量内置于闭包库 API 中,为编译器提供选项和标志。

于 2012-07-09T20:45:07.893 回答