有没有办法告诉 Google Closure Compiler *NOT* 内联我的本地函数?

Is there a way to tell Google Closure Compiler to *NOT* inline my local functions?

这是我要找的:




在问题表中:

在我的 javascript 文件顶部添加一些指令会很酷,例如:

// This is a JS comment...
// google.closure.compiler = [inlineLocalFunctions: false]

我正在开发一个 Grails 应用程序并使用 Grails asset-pipeline plugin, which uses Google Closure Compiler (hereafter, Compiler). The plugin supports the different minification levels that Compiler supports via the Grails config grails.assets.minifyOptions。这允许 'SIMPLE'、'ADVANCED'、'WHITESPACE_ONLY'.

AssetCompiler.groovy(资产管道插件)调用 ClosureCompilerProcessor.process()

最终在 CompilerOptions 对象上分配 SIMPLE_OPTIMIZATIONS。通过这样做,CompilerOptions.inlineLocalFunctions = true 作为副产品(这是编译器中的硬编码行为)。如果我使用 WHITESPACE_ONLY,结果将是 inlineLocalFunctions=false

因此,通过使用 Asset Pipeline 的 'SIMPLE' 设置,本地函数被内联,这给我带来了麻烦。示例:ExtJS ext-all-debug.js 使用大量本地函数。

SO post Is it possible to make Google Closure compiler *not* inline certain functions? 提供了一些帮助。我可以使用它的 window['dontBlowMeAway'] = dontBlowMeAway 技巧来防止我的函数内联。但是我有很多功能,我不会为每个功能手动执行此操作;我也不想写一个脚本来为我做这件事。创建 JS 模型并尝试识别本地函数听起来既不安全、有趣也不快速。

之前的 SO post 将 reader 定向到 https://developers.google.com/closure/compiler/docs/api-tutorial3#removal,其中解释了 window['bla'] 技巧,并且有效。

哇,感谢阅读这么长的时间。

帮忙? :-)


更新1:

好的。在花费所有精力写这个问题的同时,我可能有一个可行的技巧。 Grails 使用 Groovy。 Groovy 使用其 MetaClass API.

使方法调用拦截变得容易

我将尝试拦截对以下电话的呼叫:

com.google.javascript.jscomp.Compiler.compile(
    List<T1> externs, List<T2> inputs, CompilerOptions options)

我的拦截方法如下所示:

options.inlineLocalFunctions=false
// Then delegate call to the real compile() method

现在是睡觉时间,所以我必须稍后再试。即便如此,如果不用 hack 就可以解决这个问题。



更新2: 类似 post (Is it possible to make Google Closure compiler *not* inline certain functions?) 中的响应无法解决我的问题,因为我需要内联大量函数。这点我已经解释过了。

以我上面引用的ExtJS文件为例,说明为什么要执行上面的similar SO post doesn't resolve my problem. Look at the raw code for ext-all-debug.js. Find the byAttribute() function. Then keep looking for the string "byAttribute" and you'll see that it is part of strings that are being defined. I am not familiar with this code, but I'm supposing that these string-based values of byAttribute are later being passed to JS's eval()函数。当 byAttribute 是字符串的一部分时,编译器不会更改这些值。一旦 function byAttribute 被内联,就不再可能尝试调用该函数。



UPDATE3: 我尝试了两种策略来解决这个问题,但都没有成功。但是,我成功地实施了一个解决方法。我失败的尝试:

  1. 使用Groovy方法拦截(元对象协议,又名MOP)拦截com.google.javascript.jscomp.Compiler.compile().
  2. 分叉闭包-compiler.jar(制作我自己的自定义副本)并通过设置 options.setInlineFunctions(Reach.NONE); 而不是 LOCAL 来修改 com.google.javascript.jscomp.applySafeCompilationOptions()

方法拦截不起作用,因为 Compiler.compile() 是一个 Java class,它由标记为 [=30= 的 Groovy class 调用].这意味着当 process() 调用 Google 的 Compiler.compile() 时,不会使用 Groovy 的 MOP。甚至ClosureCompilerProcessor.translateMinifyOptions()(Groovy代码)也无法被拦截,因为class是@CompileStatic。唯一可以拦截的方法是ClosureCompilerProcessor.process().

分叉 Google 的闭包-compiler.jar 是我最后的丑陋手段。但就像@Chad 在下面所说的那样,简单地在正确的位置插入 options.setInlineFunctions(Reach.NONE) 并没有复活我的内联 JS 函数名称。我尝试切换其他选项,例如 setRemoveDeadCode=false 无济于事。我意识到乍得说的是对的。我最终会翻转设置并可能破坏缩小的工作方式。

我的解决方案: 我用 UglifyJS 预压缩了 ext-all-debug.js 并将它们添加到我的项目中。我本可以将文件命名为 ext-all-debug.min.js 以更干净地完成它,但我没有。以下是我在 Grails Config.groovy:

中的设置
grails.assets.minifyOptions = [
    optimizationLevel: 'SIMPLE' // WHITESPACE_ONLY, SIMPLE or ADVANCED
]

grails.assets.minifyOptions.excludes = [
    '**ext-all-debug.js',
    '**ext-theme-neptune.js'
]

完成。问题已解决。




关键字: minify, 缩小, uglify, UglifyJS, UglifyJS2

在这种情况下,您需要自定义构建编译器或使用 Java API。

但是 - 禁用内联不足以保证安全。重命名和删除死代码也会导致问题。这违反了编译器的核心假设。此本地函数仅在字符串中引用。

此代码仅对编译器的 WHITESPACE_ONLY 模式是安全的。

使用构造函数

var fnc = new Function("param1", "param2", "alert(param1+param2);");

闭包将保留字符串文字。

https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function