scala2.11在编译环节与2.10相比,默认去掉了cps和一些优化环节(phase),同时也引入了delambdafy
这个“去lambda化”的环节,见下面的22:
% scalac -Xshow-phases
phase name id description
---------- -- -----------
parser 1 parse source into ASTs, perform simple desugaring
namer 2 resolve names, attach symbols to named trees
packageobjects 3 load package objects
typer 4 the meat and potatoes: type the trees
patmat 5 translate match expressions
superaccessors 6 add super accessors in traits and nested classes
extmethods 7 add extension methods for inline classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
tailcalls 11 replace tail calls by jumps
specialize 12 @specialized-driven class and method specialization
explicitouter 13 this refs to outer pointers
erasure 14 erase types, add interfaces for traits
posterasure 15 clean up erased inline classes
lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs
lambdalift 17 move nested functions to top level
constructors 18 move field definitions into constructors
flatten 19 eliminate inner classes
mixin 20 mixin composition
cleanup 21 platform-specific cleanups, generate reflective calls
delambdafy 22 remove lambdas
icode 23 generate portable intermediate code
jvm 24 generate JVM bytecode
terminal 25 the last phase during a compilation run
通过 -Ydebug
选项可以把没有使用的phases也显示出来:
% scalac -Xshow-phases -Ydebug
...
selectiveanf xx ANF pre-transform for @cps
selectivecps xx @cps-driven transform of selectiveanf assignments
...
inliner xx optimization: do inlining
inlinehandlers xx optimization: inline exception handlers
closelim xx optimization: eliminate uncalled closures
constopt xx optimization: optimize null and other constants
dce xx optimization: eliminate dead code
上面的这些phases是2.11编译过程中默认没有启用的,而在scala2.10默认编译过程是有启用的(2.10的phases参考这里)
selectiveanf
和selectivecps
是针对cps
的,在2.11中如果要用,需要显示的增加-P:continuations:enable
编译选项;另外5个optimization也需要显示的增加-optimise
选项:
% scalac -Xshow-phases -optimise -P:continuations:enable
phase name id description
---------- -- -----------
parser 1 parse source into ASTs, perform simple desugaring
namer 2 resolve names, attach symbols to named trees
packageobjects 3 load package objects
typer 4 the meat and potatoes: type the trees
patmat 5 translate match expressions
superaccessors 6 add super accessors in traits and nested classes
extmethods 7 add extension methods for inline classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
selectiveanf 10 ANF pre-transform for @cps
selectivecps 11 @cps-driven transform of selectiveanf assignments
uncurry 12 uncurry, translate function values to anonymous classes
tailcalls 13 replace tail calls by jumps
specialize 14 @specialized-driven class and method specialization
explicitouter 15 this refs to outer pointers
erasure 16 erase types, add interfaces for traits
posterasure 17 clean up erased inline classes
lazyvals 18 allocate bitmaps, translate lazy vals into lazified defs
lambdalift 19 move nested functions to top level
constructors 20 move field definitions into constructors
flatten 21 eliminate inner classes
mixin 22 mixin composition
cleanup 23 platform-specific cleanups, generate reflective calls
delambdafy 24 remove lambdas
icode 25 generate portable intermediate code
inliner 26 optimization: do inlining
inlinehandlers 27 optimization: inline exception handlers
closelim 28 optimization: eliminate uncalled closures
constopt 29 optimization: optimize null and other constants
dce 30 optimization: eliminate dead code
jvm 31 generate JVM bytecode
terminal 32 the last phase during a compilation run
之所以默认放弃了optimization的几个选项大概是因为很难实现好,导致的问题较多收益不大(参考这里和这里);而放弃cps选项则是因为scala后续2.12将不再包含continuations
插件和库,转而采用Async来替代,因为基于CPS重写(CPS-based rewriting)的异步代码每次暂停时会产生一个闭包,同时也可能导致类型错误并且难以理解。
现在来看一下delambdafy
这个新增的phase,借用adriaanm的例子:
object Test {
def takeLambda(f: Int => Int ): Int = f(12)
def main(args: Array[String]): Unit = {
println(takeLambda(x => x+1))
println(takeLambda(x => x*2))
}
}
先不启用delambdafy
选项看看编译结果:
% scalac -print /tmp/Test.scala
...
def main(args: Array[String]): Unit = {
scala.this.Predef.println(scala.Int.box(Test.this.takeLambda({
(new <$anon: Function1>(): Function1)
})));
scala.this.Predef.println(scala.Int.box(Test.this.takeLambda({
(new <$anon: Function1>(): Function1)
})))
};
...
@SerialVersionUID(0) final <synthetic> class anonfun$main$1 extends
scala.runtime.AbstractFunction1$mcII$sp with Serializable {
final def apply(x: Int): Int = anonfun$main$1.this.apply$mcII$sp(x);
<specialized> def apply$mcII$sp(x: Int): Int = x.+(1);
final <bridge> <artifact> def apply(v1: Object): Object =
scala.Int.box(anonfun$main$1.this.apply(scala.Int.unbox(v1)));
def <init>(): <$anon: Function1> = {
anonfun$main$1.super.<init>();
()
}
};
@SerialVersionUID(0) final <synthetic> class anonfun$main$2 extends
scala.runtime.AbstractFunction1$mcII$sp with Serializable {
final def apply(x: Int): Int = anonfun$main$2.this.apply$mcII$sp(x);
<specialized> def apply$mcII$sp(x: Int): Int = x.*(2);
final <bridge> <artifact> def apply(v1: Object): Object =
scala.Int.box(anonfun$main$2.this.apply(scala.Int.unbox(v1)));
def <init>(): <$anon: Function1> = {
anonfun$main$2.super.<init>();
()
}
}
...
再看看使用delambdafy:method
选项:
% scalac -Xprint:jvm -Ydelambdafy:method /tmp/Test.scala
...
def main(args: Array[String]): Unit = {
scala.this.Predef.println(scala.Int.box(Test.this.takeLambda({
(new main1(): runtime.AbstractFunction1).$asInstanceOf[Function1]()
})));
scala.this.Predef.println(scala.Int.box(Test.this.takeLambda({
(new main2(): runtime.AbstractFunction1).$asInstanceOf[Function1]()
})))
};
final <artifact> private[this] def $anonfun$1(x: Int): Int = x.+(1);
final <artifact> private[this] def $anonfun$2(x: Int): Int = x.*(2);
final <synthetic> <static> <bridge> protected def accessor1(x: Int): Int =
Test.this.$anonfun$1(x);
final <synthetic> <static> <bridge> protected def accessor2(x: Int): Int =
Test.this.$anonfun$2(x)
...
@SerialVersionUID(0) final <synthetic> class main2 extends
runtime.AbstractFunction1 with Serializable {
<synthetic> def <init>(): main2 = {
main2.super.<init>();
()
};
final <synthetic> def apply(x: Int): Int = Test.this.accessor2(x);
final <synthetic> <bridge> def apply(x: Object): Object =
scala.Int.box(main2.this.apply(unbox(x)))
};
@SerialVersionUID(0) final <synthetic> class main1 extends
runtime.AbstractFunction1 with Serializable {
<synthetic> def <init>(): main1 = {
main1.super.<init>();
()
};
final <synthetic> def apply(x: Int): Int = Test.this.accessor1(x);
final <synthetic> <bridge> def apply(x: Object): Object =
scala.Int.box(main1.this.apply(unbox(x)))
}
在开启delambdafy:method
选项之后,原先在匿名类中实现的方法,被移到了当前对象中,并提供了静态accessor方法,在scala.tools.nsc.transform.Delambdafy
的文档里是这么描述的:
1) a static forwarder at the top level of the class that contained the lambda
2) a new top level class that
a) has fields and a constructor taking the captured environment
b) an apply method that calls the static forwarder
c) if needed a bridge method for the apply method
3) an instantiation of the newly created class which replaces the lambda
目前还是一个实验性质,这个做法更接近用method handles,显然是为了下一步支持jsr292做准备的。
scala2.11字节码仍兼容jdk1.6,不会采用invokedynamic
,而scala2.12的字节码会直接跳到 jdk1.8。
http://www.scala-lang.org/news/2014/03/06/release-notes-2.11.0-RC1.html
A new experimental way of compiling closures, implemented by @JamesIry. With -Ydelambdafy:method anonymous functions are compiled faster, with a smaller bytecode footprint. This works by keeping the function body as a private (static, if no this reference is needed) method of the enclosing class, and at the last moment during compilation emitting a small anonymous class that extends FunctionN and delegates to it. This sets the scene for a smooth migration to Java 8-style lambdas (not yet implemented).
Ydelambdafy:method 这个貌似是实验性功能,不知道会不会出一些奇葩的bug
不排除