forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBackendUtils.scala
191 lines (172 loc) · 7.54 KB
/
BackendUtils.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package dotty.tools.backend.jvm
import scala.tools.asm
import scala.tools.asm.Handle
import scala.tools.asm.tree.InvokeDynamicInsnNode
import asm.tree.ClassNode
import scala.collection.mutable
import scala.jdk.CollectionConverters.*
import dotty.tools.dotc.report
import scala.language.unsafeNulls
/**
* This component hosts tools and utilities used in the backend that require access to a `BTypes`
* instance.
*/
class BackendUtils(val postProcessor: PostProcessor) {
import postProcessor.{bTypes, frontendAccess}
import frontendAccess.{compilerSettings}
import bTypes.*
import coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle
lazy val classfileVersion: Int = BackendUtils.classfileVersionMap(compilerSettings.target.toInt)
lazy val extraProc: Int = {
import GenBCodeOps.addFlagIf
val majorVersion: Int = (classfileVersion & 0xFF)
val emitStackMapFrame = (majorVersion >= 50)
asm.ClassWriter.COMPUTE_MAXS
.addFlagIf(emitStackMapFrame, asm.ClassWriter.COMPUTE_FRAMES)
}
def collectSerializableLambdas(classNode: ClassNode): Array[Handle] = {
val indyLambdaBodyMethods = new mutable.ArrayBuffer[Handle]
for (m <- classNode.methods.asScala) {
val iter = m.instructions.iterator
while (iter.hasNext) {
val insn = iter.next()
insn match {
case indy: InvokeDynamicInsnNode
if indy.bsm == jliLambdaMetaFactoryAltMetafactoryHandle =>
import java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE
val metafactoryFlags = indy.bsmArgs(3).asInstanceOf[Integer].toInt
val isSerializable = (metafactoryFlags & FLAG_SERIALIZABLE) != 0
if isSerializable then
val implMethod = indy.bsmArgs(1).asInstanceOf[Handle]
indyLambdaBodyMethods += implMethod
case _ =>
}
}
}
indyLambdaBodyMethods.toArray
}
/*
* Add:
*
* private static Object $deserializeLambda$(SerializedLambda l) {
* try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$0](l)
* catch {
* case i: IllegalArgumentException =>
* try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$1](l)
* catch {
* case i: IllegalArgumentException =>
* ...
* return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup${NUM_GROUPS-1}](l)
* }
*
* We use invokedynamic here to enable caching within the deserializer without needing to
* host a static field in the enclosing class. This allows us to add this method to interfaces
* that define lambdas in default methods.
*
* SI-10232 we can't pass arbitrary number of method handles to the final varargs parameter of the bootstrap
* method due to a limitation in the JVM. Instead, we emit a separate invokedynamic bytecode for each group of target
* methods.
*/
def addLambdaDeserialize(classNode: ClassNode, implMethodsArray: Array[Handle]): Unit = {
import asm.Opcodes.*
import bTypes.*
import coreBTypes.*
val cw = classNode
// Make sure to reference the ClassBTypes of all types that are used in the code generated
// here (e.g. java/util/Map) are initialized. Initializing a ClassBType adds it to
// `classBTypeFromInternalNameMap`. When writing the classfile, the asm ClassWriter computes
// stack map frames and invokes the `getCommonSuperClass` method. This method expects all
// ClassBTypes mentioned in the source code to exist in the map.
val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serializedLamdaObjDesc, null, null)
def emitLambdaDeserializeIndy(targetMethods: Seq[Handle]): Unit = {
mv.visitVarInsn(ALOAD, 0)
mv.visitInvokeDynamicInsn("lambdaDeserialize", serializedLamdaObjDesc, jliLambdaDeserializeBootstrapHandle, targetMethods: _*)
}
val targetMethodGroupLimit = 255 - 1 - 3 // JVM limit. See See MAX_MH_ARITY in CallSite.java
val groups: Array[Array[Handle]] = implMethodsArray.grouped(targetMethodGroupLimit).toArray
val numGroups = groups.length
import scala.tools.asm.Label
val initialLabels = Array.fill(numGroups - 1)(new Label())
val terminalLabel = new Label
def nextLabel(i: Int) = if (i == numGroups - 2) terminalLabel else initialLabels(i + 1)
for ((label, i) <- initialLabels.iterator.zipWithIndex) {
mv.visitTryCatchBlock(label, nextLabel(i), nextLabel(i), jlIllegalArgExceptionRef.internalName)
}
for ((label, i) <- initialLabels.iterator.zipWithIndex) {
mv.visitLabel(label)
emitLambdaDeserializeIndy(groups(i).toIndexedSeq)
mv.visitInsn(ARETURN)
}
mv.visitLabel(terminalLabel)
emitLambdaDeserializeIndy(groups(numGroups - 1).toIndexedSeq)
mv.visitInsn(ARETURN)
}
private lazy val serializedLamdaObjDesc = {
import coreBTypes.{ObjectRef, jliSerializedLambdaRef}
MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor
}
/**
* Visit the class node and collect all referenced nested classes.
*/
def collectNestedClasses(classNode: ClassNode): (List[ClassBType], List[ClassBType]) = {
// type InternalName = String
val c = new NestedClassesCollector[ClassBType](nestedOnly = true) {
def declaredNestedClasses(internalName: InternalName): List[ClassBType] =
bTypes.classBTypeFromInternalName(internalName).info.memberClasses
def getClassIfNested(internalName: InternalName): Option[ClassBType] = {
val c = bTypes.classBTypeFromInternalName(internalName)
Option.when(c.isNestedClass)(c)
}
def raiseError(msg: String, sig: String, e: Option[Throwable]): Unit = {
// don't crash on invalid generic signatures
}
}
c.visit(classNode)
(c.declaredInnerClasses.toList, c.referredInnerClasses.toList)
}
/*
* Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner
* classes in BTypes.scala.
*
* `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of
* each inner class it lists (those are looked up and included).
*
* This method serializes in the InnerClasses JVM attribute in an appropriate order,
* not necessarily that given by `refedInnerClasses`.
*
* can-multi-thread
*/
final def addInnerClasses(jclass: asm.ClassVisitor, declaredInnerClasses: List[ClassBType], refedInnerClasses: List[ClassBType]): Unit = {
// sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
val allNestedClasses = new mutable.TreeSet[ClassBType]()(Ordering.by(_.internalName))
allNestedClasses ++= declaredInnerClasses
refedInnerClasses.foreach(allNestedClasses ++= _.enclosingNestedClassesChain)
for nestedClass <- allNestedClasses
do {
// Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes.
val Some(e) = nestedClass.innerClassAttributeEntry: @unchecked
jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags)
}
}
}
object BackendUtils {
lazy val classfileVersionMap: Map[Int, Int] = Map(
8 -> asm.Opcodes.V1_8,
9 -> asm.Opcodes.V9,
10 -> asm.Opcodes.V10,
11 -> asm.Opcodes.V11,
12 -> asm.Opcodes.V12,
13 -> asm.Opcodes.V13,
14 -> asm.Opcodes.V14,
15 -> asm.Opcodes.V15,
16 -> asm.Opcodes.V16,
17 -> asm.Opcodes.V17,
18 -> asm.Opcodes.V18,
19 -> asm.Opcodes.V19,
20 -> asm.Opcodes.V20,
21 -> asm.Opcodes.V21,
22 -> asm.Opcodes.V22,
23 -> asm.Opcodes.V23,
24 -> asm.Opcodes.V24
)
}