Closure Compiler并不总是内联枚举。为什么?

时间:2023-01-06 01:41:24

What are the rules that Google's Closure Compiler follows for deciding if an enum is going to be inline or not?

Google的Closure Compiler遵循哪些规则来决定枚举是否内联?

When I run my code through the Closure Compiler the JSDoc annotation enum type is not inlined. Yet when I create a simplified example, the enum types are inlined, as this nonsense example will demonstrate:

当我通过Closure Compiler运行我的代码时,JSDoc注释枚举类型没有内联。然而,当我创建一个简化的示例时,枚举类型是内联的,因为这个无意义的示例将演示:

 var my_name_space = (function () {
     'use strict';

     /** @enum {number} */
     var TASK_STATUS = {
         REJECT: -1,
         UNKNOWN: 0,
         APPROVE: 1
     };

     function init_(a) {
         if (a === TASK_STATUS.UNKNOWN) {
             alert("Reject");
             a = TASK_STATUS.REJECT;
         } else if (a === TASK_STATUS.APPROVE) {
             alert("Unknown");
             a = TASK_STATUS.UNKNOWN;
         } else {
             alert("Approve");
             a = TASK_STATUS.APPROVE;
         }
         return a;
     }
     return { init: init_};

 }());  // my_name_space


 my_name_space.init(-1);

Closure's output:

关闭的输出:

 var my_name_space=function(){return{init:function(a){0===a?(alert("Reject"),a=-1):1===a?(alert("Unknown"),a=0):(alert("Approve"),a=1);return a}}}();my_name_space.init(-1);

In fact, in lining will occur with or without the JSDoc header.

事实上,衬里会发生有或没有JSDoc头。

Please explain under what conditions in lining would not occur, or even better, please make modification(s) to the above that would demonstrate when lining would not happen.

请解释衬里在什么条件下不会发生,甚至更好,请对上述内容进行修改,以证明衬里不会发生。

I'm using 'Simple' optimization level.

我正在使用'简单'优化级别。

2 个解决方案

#1


3  

First, enums aren't really special objects for optimization purposes, however, @enum is useful for type checking and has special rules for that purpose.

首先,枚举不是用于优化目的的特殊对象,但是,@ enum对于类型检查很有用,并且具有用于该目的的特殊规则。

As for inlining, there are many things that would prevent inlining, it isn't reasonable to demonstrate all the possibilities, but I can give you an idea as to why they might not be:

至于内联,有很多东西会阻止内联,展示所有可能性是不合理的,但我可以告诉你为什么它们可能不是:

  • The enum object is defined as a property on object that can't be decomposed into variable (ADVANCED mode helps here)
  • 枚举对象被定义为对象上的属性,无法分解为变量(ADVANCED模式有助于此处)
  • The enum is defined on a global variable (ADVANCED mode helps here)
  • 枚举是在全局变量上定义的(ADVANCED模式有帮助)
  • The enum object is aliased in a way that the alias itself can't be removed (passing the enum object to a function that can't be inlined, assigning the enum object to a global value).
  • 枚举对象的别名方式是别名本身无法删除(将枚举对象传递给无法内联的函数,将枚举对象赋值为全局值)。
  • Iterating over the enum keys/values (for-in, etc)
  • 迭代枚举键/值(for-in等)
  • The enum object is overwritten
  • 枚举enum对象

These all come down to not the compiler being able to determine that it can:

这些都归结为编译器无法确定它可以:

  • remove the object and replace it with individual values
  • 删除对象并将其替换为单个值
  • inline those values
  • 内联这些值

That said, if you have a simple local definition, and you only ever reference the values and not the object itself, and the values themselves are simple constants (numbers, booleans), it is always going to be inlined.

也就是说,如果你有一个简单的局部定义,并且你只引用值而不是对象本身,并且值本身是简单的常量(数字,布尔值),它总是会被内联。

#2


1  

First off, I believe you'll have to use ADVANCED_OPTIMIZATIONS to get any wins from inlining. Your current code isn't valid for ADVANCED OPTIMIZATIONS currently, so I modified to make it work.

首先,我相信你必须使用ADVANCED_OPTIMIZATIONS来获得内联的任何胜利。您当前的代码目前对ADVANCED OPTIMIZATIONS无效,因此我进行了修改以使其正常工作。

 /** @enum {number} @const */
 var TASK_STATUS = {
     REJECT: -1,
     UNKNOWN: 0,
     APPROVE: 1
 };

 /**
  *  @param {TASK_STATUS} a
  */
 function init_(a) {
     if (a === TASK_STATUS.UNKNOWN) {
         alert("Reject");
     } else if (a === TASK_STATUS.APPROVE) {
         alert("Unknown");
     } else {
         alert("Approve");
     }
 }

init_(TASK_STATUS.REJECT);

outputs:

输出:

alert("Approve");

The reason why closure compiler can't inline your code is for a few different reasons. Closure compiler doesn't dig all the way into your code -- it generally uses type information that you pass in to make decisions. It doesn't do well with passing functions via object expressions. (ie, passing the init function back via object literal)

闭包编译器无法内联代码的原因有几个原因。 Closure编译器不会一直深入到您的代码中 - 它通常使用您传入的类型信息来做出决策。通过对象表达式传递函数效果不佳。 (即,通过对象文字传回init函数)

Secondly, your init function had side effects (assignment of a). Since closure compiler didn't know what was really being changed, (no type information), it did the safe thing and didn't inline the function out.

其次,你的init函数有副作用(分配a)。由于闭包编译器不知道实际改变了什么,(没有类型信息),它做了安全的事情并没有内联函数。

Closure compiler in advanced mode is a beast. It will take some getting used to.

高级模式下的Closure编译器是一个野兽。这需要一些时间来适应。

#1


3  

First, enums aren't really special objects for optimization purposes, however, @enum is useful for type checking and has special rules for that purpose.

首先,枚举不是用于优化目的的特殊对象,但是,@ enum对于类型检查很有用,并且具有用于该目的的特殊规则。

As for inlining, there are many things that would prevent inlining, it isn't reasonable to demonstrate all the possibilities, but I can give you an idea as to why they might not be:

至于内联,有很多东西会阻止内联,展示所有可能性是不合理的,但我可以告诉你为什么它们可能不是:

  • The enum object is defined as a property on object that can't be decomposed into variable (ADVANCED mode helps here)
  • 枚举对象被定义为对象上的属性,无法分解为变量(ADVANCED模式有助于此处)
  • The enum is defined on a global variable (ADVANCED mode helps here)
  • 枚举是在全局变量上定义的(ADVANCED模式有帮助)
  • The enum object is aliased in a way that the alias itself can't be removed (passing the enum object to a function that can't be inlined, assigning the enum object to a global value).
  • 枚举对象的别名方式是别名本身无法删除(将枚举对象传递给无法内联的函数,将枚举对象赋值为全局值)。
  • Iterating over the enum keys/values (for-in, etc)
  • 迭代枚举键/值(for-in等)
  • The enum object is overwritten
  • 枚举enum对象

These all come down to not the compiler being able to determine that it can:

这些都归结为编译器无法确定它可以:

  • remove the object and replace it with individual values
  • 删除对象并将其替换为单个值
  • inline those values
  • 内联这些值

That said, if you have a simple local definition, and you only ever reference the values and not the object itself, and the values themselves are simple constants (numbers, booleans), it is always going to be inlined.

也就是说,如果你有一个简单的局部定义,并且你只引用值而不是对象本身,并且值本身是简单的常量(数字,布尔值),它总是会被内联。

#2


1  

First off, I believe you'll have to use ADVANCED_OPTIMIZATIONS to get any wins from inlining. Your current code isn't valid for ADVANCED OPTIMIZATIONS currently, so I modified to make it work.

首先,我相信你必须使用ADVANCED_OPTIMIZATIONS来获得内联的任何胜利。您当前的代码目前对ADVANCED OPTIMIZATIONS无效,因此我进行了修改以使其正常工作。

 /** @enum {number} @const */
 var TASK_STATUS = {
     REJECT: -1,
     UNKNOWN: 0,
     APPROVE: 1
 };

 /**
  *  @param {TASK_STATUS} a
  */
 function init_(a) {
     if (a === TASK_STATUS.UNKNOWN) {
         alert("Reject");
     } else if (a === TASK_STATUS.APPROVE) {
         alert("Unknown");
     } else {
         alert("Approve");
     }
 }

init_(TASK_STATUS.REJECT);

outputs:

输出:

alert("Approve");

The reason why closure compiler can't inline your code is for a few different reasons. Closure compiler doesn't dig all the way into your code -- it generally uses type information that you pass in to make decisions. It doesn't do well with passing functions via object expressions. (ie, passing the init function back via object literal)

闭包编译器无法内联代码的原因有几个原因。 Closure编译器不会一直深入到您的代码中 - 它通常使用您传入的类型信息来做出决策。通过对象表达式传递函数效果不佳。 (即,通过对象文字传回init函数)

Secondly, your init function had side effects (assignment of a). Since closure compiler didn't know what was really being changed, (no type information), it did the safe thing and didn't inline the function out.

其次,你的init函数有副作用(分配a)。由于闭包编译器不知道实际改变了什么,(没有类型信息),它做了安全的事情并没有内联函数。

Closure compiler in advanced mode is a beast. It will take some getting used to.

高级模式下的Closure编译器是一个野兽。这需要一些时间来适应。