@@ -32,6 +32,11 @@ import scala.tools.nsc.settings.ScalaSettings
32
32
abstract class BTypes {
33
33
import BTypes .InternalName
34
34
35
+ // Stages after code generation in the backend (optimizations, classfile writing) are prepared
36
+ // to run in parallel on multiple classes. This object should be used for synchronizing operations
37
+ // that may access the compiler frontend during these late stages.
38
+ val frontendLock : AnyRef = new Object ()
39
+
35
40
val backendUtils : BackendUtils [this .type ]
36
41
37
42
// Some core BTypes are required here, in class BType, where no Global instance is available.
@@ -64,17 +69,19 @@ abstract class BTypes {
64
69
def compilerSettings : ScalaSettings
65
70
66
71
/**
67
- * A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
68
- * construction.
72
+ * Every ClassBType is cached on construction and accessible through this method.
69
73
*
70
- * This map is used when computing stack map frames. The asm.ClassWriter invokes the method
74
+ * The cache is used when computing stack map frames. The asm.ClassWriter invokes the method
71
75
* `getCommonSuperClass`. In this method we need to obtain the ClassBType for a given internal
72
- * name. The method assumes that every class type that appears in the bytecode exists in the map.
73
- *
74
- * Concurrent because stack map frames are computed when in the class writer, which might run
75
- * on multiple classes concurrently.
76
+ * name. The method assumes that every class type that appears in the bytecode exists in the map
76
77
*/
77
- val classBTypeFromInternalName : concurrent.Map [InternalName , ClassBType ] = recordPerRunCache(TrieMap .empty)
78
+ def cachedClassBType (internalName : InternalName ): Option [ClassBType ] =
79
+ classBTypeCacheFromSymbol.get(internalName).orElse(classBTypeCacheFromClassfile.get(internalName))
80
+
81
+ // Concurrent maps because stack map frames are computed when in the class writer, which
82
+ // might run on multiple classes concurrently.
83
+ val classBTypeCacheFromSymbol : concurrent.Map [InternalName , ClassBType ] = recordPerRunCache(TrieMap .empty)
84
+ val classBTypeCacheFromClassfile : concurrent.Map [InternalName , ClassBType ] = recordPerRunCache(TrieMap .empty)
78
85
79
86
/**
80
87
* Store the position of every MethodInsnNode during code generation. This allows each callsite
@@ -173,8 +180,8 @@ abstract class BTypes {
173
180
* be found in the `byteCodeRepository`, the `info` of the resulting ClassBType is undefined.
174
181
*/
175
182
def classBTypeFromParsedClassfile (internalName : InternalName ): ClassBType = {
176
- classBTypeFromInternalName .getOrElse(internalName, {
177
- val res = ClassBType (internalName)
183
+ cachedClassBType(internalName) .getOrElse({
184
+ val res = ClassBType (internalName)(classBTypeCacheFromClassfile)
178
185
byteCodeRepository.classNode(internalName) match {
179
186
case Left (msg) => res.info = Left (NoClassBTypeInfoMissingBytecode (msg)); res
180
187
case Right (c) => setClassInfoFromClassNode(c, res)
@@ -186,8 +193,8 @@ abstract class BTypes {
186
193
* Construct the [[ClassBType ]] for a parsed classfile.
187
194
*/
188
195
def classBTypeFromClassNode (classNode : ClassNode ): ClassBType = {
189
- classBTypeFromInternalName.getOrElse (classNode.name, {
190
- setClassInfoFromClassNode(classNode, ClassBType (classNode.name))
196
+ cachedClassBType (classNode.name).getOrElse( {
197
+ setClassInfoFromClassNode(classNode, ClassBType (classNode.name)(classBTypeCacheFromClassfile) )
191
198
})
192
199
}
193
200
@@ -221,13 +228,13 @@ abstract class BTypes {
221
228
})
222
229
}
223
230
224
- val nestedClasses : List [ClassBType ] = classNode.innerClasses.asScala.collect({
231
+ def nestedClasses : List [ClassBType ] = classNode.innerClasses.asScala.collect({
225
232
case i if nestedInCurrentClass(i) => classBTypeFromParsedClassfile(i.name)
226
233
})(collection.breakOut)
227
234
228
235
// if classNode is a nested class, it has an innerClass attribute for itself. in this
229
236
// case we build the NestedInfo.
230
- val nestedInfo = classNode.innerClasses.asScala.find(_.name == classNode.name) map {
237
+ def nestedInfo = classNode.innerClasses.asScala.find(_.name == classNode.name) map {
231
238
case innerEntry =>
232
239
val enclosingClass =
233
240
if (innerEntry.outerName != null ) {
@@ -246,7 +253,7 @@ abstract class BTypes {
246
253
247
254
val interfaces : List [ClassBType ] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
248
255
249
- classBType.info = Right (ClassInfo (superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
256
+ classBType.info = Right (ClassInfo (superClass, interfaces, flags, Lazy ( nestedClasses), Lazy ( nestedInfo) , inlineInfo))
250
257
classBType
251
258
}
252
259
@@ -857,7 +864,7 @@ abstract class BTypes {
857
864
* a missing info. In order not to crash the compiler unnecessarily, the inliner does not force
858
865
* infos using `get`, but it reports inliner warnings for missing infos that prevent inlining.
859
866
*/
860
- final case class ClassBType (internalName : InternalName ) extends RefBType {
867
+ final case class ClassBType (internalName : InternalName )( cache : mutable. Map [ InternalName , ClassBType ]) extends RefBType {
861
868
/**
862
869
* Write-once variable allows initializing a cyclic graph of infos. This is required for
863
870
* nested classes. Example: for the definition `class A { class B }` we have
@@ -878,7 +885,7 @@ abstract class BTypes {
878
885
checkInfoConsistency()
879
886
}
880
887
881
- classBTypeFromInternalName (internalName) = this
888
+ cache (internalName) = this
882
889
883
890
private def checkInfoConsistency (): Unit = {
884
891
if (info.isLeft) return
@@ -903,7 +910,9 @@ abstract class BTypes {
903
910
s " Invalid interfaces in $this: ${info.get.interfaces}"
904
911
)
905
912
906
- assert(info.get.nestedClasses.forall(c => ifInit(c)(_.isNestedClass.get)), info.get.nestedClasses)
913
+ info.get.nestedClasses.onForce { cs =>
914
+ assert(cs.forall(c => ifInit(c)(_.isNestedClass.get)), cs)
915
+ }
907
916
}
908
917
909
918
/**
@@ -931,17 +940,17 @@ abstract class BTypes {
931
940
932
941
def isPublic : Either [NoClassBTypeInfo , Boolean ] = info.map(i => (i.flags & asm.Opcodes .ACC_PUBLIC ) != 0 )
933
942
934
- def isNestedClass : Either [NoClassBTypeInfo , Boolean ] = info.map(_.nestedInfo.isDefined)
943
+ def isNestedClass : Either [NoClassBTypeInfo , Boolean ] = info.map(_.nestedInfo.force. isDefined)
935
944
936
945
def enclosingNestedClassesChain : Either [NoClassBTypeInfo , List [ClassBType ]] = {
937
946
isNestedClass.flatMap(isNested => {
938
947
// if isNested is true, we know that info.get is defined, and nestedInfo.get is also defined.
939
- if (isNested) info.get.nestedInfo.get.enclosingClass.enclosingNestedClassesChain.map(this :: _)
948
+ if (isNested) info.get.nestedInfo.force. get.enclosingClass.enclosingNestedClassesChain.map(this :: _)
940
949
else Right (Nil )
941
950
})
942
951
}
943
952
944
- def innerClassAttributeEntry : Either [NoClassBTypeInfo , Option [InnerClassEntry ]] = info.map(i => i.nestedInfo map {
953
+ def innerClassAttributeEntry : Either [NoClassBTypeInfo , Option [InnerClassEntry ]] = info.map(i => i.nestedInfo.force map {
945
954
case NestedInfo (_, outerName, innerName, isStaticNestedClass) =>
946
955
InnerClassEntry (
947
956
internalName,
@@ -1074,9 +1083,49 @@ abstract class BTypes {
1074
1083
* @param inlineInfo Information about this class for the inliner.
1075
1084
*/
1076
1085
final case class ClassInfo (superClass : Option [ClassBType ], interfaces : List [ClassBType ], flags : Int ,
1077
- nestedClasses : List [ClassBType ], nestedInfo : Option [NestedInfo ],
1086
+ nestedClasses : Lazy [ List [ClassBType ]] , nestedInfo : Lazy [ Option [NestedInfo ] ],
1078
1087
inlineInfo : InlineInfo )
1079
1088
1089
+ object Lazy {
1090
+ def apply [T <: AnyRef ](t : => T ): Lazy [T ] = new Lazy [T ](() => t)
1091
+ }
1092
+
1093
+ final class Lazy [T <: AnyRef ](t : () => T ) {
1094
+ private var value : T = null .asInstanceOf [T ]
1095
+
1096
+ private var function = {
1097
+ val tt = t // prevent allocating a field for t
1098
+ () => { value = tt() }
1099
+ }
1100
+
1101
+ override def toString = if (value == null ) " <?>" else value.toString
1102
+
1103
+ def onForce (f : T => Unit ): Unit = {
1104
+ if (value != null ) f(value)
1105
+ else frontendLock.synchronized {
1106
+ if (value != null ) f(value)
1107
+ else {
1108
+ val prev = function
1109
+ function = () => {
1110
+ prev()
1111
+ f(value)
1112
+ }
1113
+ }
1114
+ }
1115
+ }
1116
+
1117
+ def force : T = {
1118
+ if (value != null ) value
1119
+ else frontendLock.synchronized {
1120
+ if (value == null ) {
1121
+ function()
1122
+ function = null
1123
+ }
1124
+ value
1125
+ }
1126
+ }
1127
+ }
1128
+
1080
1129
/**
1081
1130
* Information required to add a class to an InnerClass table.
1082
1131
* The spec summary above explains what information is required for the InnerClass entry.
0 commit comments