有没有办法告诉Google Closure Compiler *不*内联我的本地功能? [英] Is there a way to tell Google Closure Compiler to *NOT* inline my local functions?

查看:97
本文介绍了有没有办法告诉Google Closure Compiler *不*内联我的本地功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用美妙的功能SIMPLE模式缩小,同时仅禁用一个特定功能(禁用本地函数内联)。 p>


  • 更新:答案是否定的,因为我的设置不可能。但是对于我来说,我使用Grails。

  • 正如@Chad在下面解释的,这违反了编译器的核心假设。






问题表单中:


  • 我使用 CompilationLevel.SIMPLE_OPTIMIZATIONS 它可以完成我想要的任何操作,只不过它内联了我的本地函数。

  • 有没有办法解决这个问题?例如,是否可以在我的JS文件中放置一个设置来告诉Google Closure不要内联我的本地函数?


它会在我的javascript文件的顶部添加一些指令,比如:b / b
$ b pre $ //这是一个JS注释...
// google.closure.compiler = [inlineLocalFunctions:false]

我正在开发Grails应用程序并使用使用Google Closure Compiler的 Grails asset-pipeline 插件(以下简称编译器)。该插件支持编译器通过Grails配置 grails支持的不同缩小级别。 assets.minifyOptions 的。这允许'SIMPLE','ADVANCED','WHITESPACE_ONLY'。

AssetCompiler.groovy(asset-pipeline插件)调用 ClosureCompilerProcessor.process )



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



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



SO post 是否可以让Google Closure编译器*不内联某些函数?提供了一些帮助。我可以使用它的窗口['dontBlowMeAway'] = dontBlowMeAway 窍门让我的函数不会内联。不过,我有很多功能,我不打算为每个功能手动执行此操作;我也不想写一个脚本来为我做。创建JS模型并尝试标识本地函数听起来并不安全,有趣也不快。



以前的SO文章指导读者 https://developers.google.com/closure/compiler/docs/api-tutorial3#removal ,其中解释了窗口['bla'] 技巧,并且它可以工作。

哇谢谢阅读这个很长。

帮助? : - )




UPDATE1:

好。在花费所有的精力写这个问题的时候,我可能会有一个可行的窍门。 Grails使用Groovy。



我会尝试拦截调用:

  com.google.javascript.jscomp.Compiler.compile(
列出< T1> externs,List< T2>输入,CompilerOptions选项)

我的拦截方法如下所示:

  options.inlineLocalFunctions = false 
//然后委托调用实际的compile()方法



<现在是睡觉时间,所以我得晚点再试。即使如此,最好不用破解就能解决这个问题。









UPDATE2:
类似帖子中的回复(是否有可能让Google Closure编译器*不*内联某些函数?)不能解决我的问题,因为我需要内联的大量功能。我已经解释了这一点。



以上面引用的ExtJS文件为例说明为什么上述类似的SO帖子不能解决我的问题。查看 ext-all-debug的原始代码。 JS 。找到byAttribute()函数。然后继续查找字符串byAttribute,你会发现它是正在定义的字符串的一部分。我对这段代码并不熟悉,但我假设 byAttribute 的这些基于字符串的值稍后会传递给 JS的eval()函数执行。当它是字符串的一部分时,编译器不会改变 byAttribute 的这些值。一旦函数byAttribute 被内联,将不再可能尝试调用该函数。



p>




UPDATE3:我尝试了两种策略来解决此问题,并且都证明不成功。但是,我成功实施了一种解决方法。我失败的尝试:


  1. 使用Groovy方法拦截(Meta Object Protocol,又名MOP)截取 com.google。 javascript.jscomp.Compiler.compile()

  2. 分离closure-compiler.jar(创建我自己的自定义副本)并修改 com.google.javascript.jscomp.applySafeCompilationOptions()通过设置 options.setInlineFunctions(Reach.NONE); 而不是LOCAL。

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



分叉Google的closure-compiler.jar是我最后一个丑陋的手段。但就像下面的@Chad所说的那样,只要在正确的位置插入 options.setInlineFunctions(Reach.NONE)就不会重新生成我的内联JS函数名称。我试图切换其他选项,如 setRemoveDeadCode = false 无济于事。我意识到乍得说的是对的。我最终会改变设置并可能破坏缩小的工作方式。



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

pre $ grails.assets.minifyOptions = [
optimizationLevel: 'SIMPLE'// WHITESPACE_ONLY,SIMPLE或ADVANCED
]

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

完成。问题解决了。









关键字:minify,minification,uglify, UglifyJS,UglifyJS2

解决方案

在这种情况下,您可能需要编译器的自定义构建或使用Java API。然而,禁用内联并不足以保证安全。重命名和无效代码消除也会导致问题。这违反了编译器的核心假设。这个本地函数只能从字符串中引用。

这个代码只对编译器的 WHITESPACE_ONLY 模式安全。

Here's what I'm looking for:

  • I want to use the wonderful features of SIMPLE mode minification while disabling just one specific feature (disable local function inline).


  • UPDATE: The answer is NO, it's not possible given my setup. But for me there is a workaround given I am using Grails.
  • As @Chad has explained below, "This violates core assumptions of the compiler". See my UPDATE3 below for more info.



IN QUESTION FORM:

  • I'm using CompilationLevel.SIMPLE_OPTIMIZATIONS which does everything I want, except that it's inlining my local functions.
  • Is there any way around this? For example, is there a setting I can place in my JS files to tell Google Closure not to inline my local functions?

It would be cool to have some directives at the top of my javascript file such as:

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

I'm developing a Grails app and using the 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. This allows for 'SIMPLE', 'ADVANCED', 'WHITESPACE_ONLY'.

AssetCompiler.groovy (asset-pipeline plugin) calls ClosureCompilerProcessor.process()

That eventually assigns SIMPLE_OPTIMIZATIONS on the CompilerOptions object. And by doing so, CompilerOptions.inlineLocalFunctions = true as a byproduct (this is hard coded behavior in Compiler). If I were to use WHITESPACE_ONLY the result would be inlineLocalFunctions=false.

So by using Asset Pipeline's 'SIMPLE' setting, local functions are being inlined and that is causing me trouble. Example: ExtJS ext-all-debug.js which uses lots of local functions.

SO post Is it possible to make Google Closure compiler *not* inline certain functions? provides some help. I can use its window['dontBlowMeAway'] = dontBlowMeAway trick to keep my functions from inlining. However I have LOTS of functions and I'm not about to manually do this for each one; nor would I want to write a script to do it for me. Creating a JS model and trying to identity local functions doesn't sound safe, fun nor fast.

The previous SO post directs the reader to https://developers.google.com/closure/compiler/docs/api-tutorial3#removal, where the window['bla'] trick is explained, and it works.

Wow thanks for reading this long.

Help? :-)


UPDATE1:

Okay. While spending all the effort in writing this question, I may have a trick that could work. Grails uses Groovy. Groovy makes method call interception easy using its MetaClass API.

I'm going to try intercepting the call to:

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

My intercepting method will look like:

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

It's bed time so I'll have to try this later. Even so, it would be nice to solve this without a hack.



UPDATE2: The response in a similar post (Is it possible to make Google Closure compiler *not* inline certain functions?) doesn't resolve my problem because of the large quantity of functions I need inlined. I've already explained this point.

Take the ExtJS file I cited above as an example of why the above 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() function for execution. Compiler does not alter these values of byAttribute when it's part of a string. Once function byAttribute is inlined, attempts to call the function is no longer possible.



UPDATE3: I attempted two strategies to resolve this problem and both proved unsuccessful. However, I successfully implemented a workaround. My failed attempts:

  1. Use Groovy method interception (Meta Object Protocol, aka MOP) to intercept com.google.javascript.jscomp.Compiler.compile().
  2. Fork the closure-compiler.jar (make my own custom copy) and modify com.google.javascript.jscomp.applySafeCompilationOptions() by setting options.setInlineFunctions(Reach.NONE); instead of LOCAL.

Method interception doesn't work because Compiler.compile() is a Java class which is invoked by a Groovy class marked as @CompileStatic. That means Groovy's MOP is not used when process() calls Google's Compiler.compile(). Even ClosureCompilerProcessor.translateMinifyOptions() (Groovy code) can't be intercepted because the class is @CompileStatic. The only method that can be intercepted is ClosureCompilerProcessor.process().

Forking Google's closure-compiler.jar was my last ugly resort. But just like @Chad said below, simply inserting options.setInlineFunctions(Reach.NONE) in the right place didn't resurrect my inline JS functions names. I tried toggling other options such as setRemoveDeadCode=false to no avail. I realized what Chad said was right. I would end up flipping settings around and probably destroying how the minification works.

My solution: I pre-compressed ext-all-debug.js with UglifyJS and added them to my project. I could have named the files ext-all-debug.min.js to do it more cleanly but I didn't. Below are the settings I placed in my 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'
]

Done. Problem solved.




Keywords: minify, minification, uglify, UglifyJS, UglifyJS2

解决方案

In this case, you would either need to make a custom build of the compiler or use the Java API.

However - disabling inlining is not enough to make this safe. Renaming and dead code elimination will also cause problems. This violates core assumptions of the compiler. This local function is ONLY referenced from within strings.

This code is only safe for the WHITESPACE_ONLY mode of the compiler.

这篇关于有没有办法告诉Google Closure Compiler *不*内联我的本地功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆