Skip to content

Commit a80f6a0

Browse files
committed
Merge pull request scala#4896 from retronym/topic/indy-all-the-things
Use invokedynamic for structural calls, symbol literals, lambda ser.
2 parents 6d09a1b + df0d105 commit a80f6a0

File tree

20 files changed

+363
-188
lines changed

20 files changed

+363
-188
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,15 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
310310
case app : Apply =>
311311
generatedType = genApply(app, expectedType)
312312

313+
case app @ ApplyDynamic(qual, Literal(Constant(boostrapMethodRef: Symbol)) :: staticAndDynamicArgs) =>
314+
val numStaticArgs = boostrapMethodRef.paramss.head.size - 3 /*JVM provided args*/
315+
val (staticArgs, dynamicArgs) = staticAndDynamicArgs.splitAt(numStaticArgs)
316+
val boostrapDescriptor = staticHandleFromSymbol(boostrapMethodRef)
317+
val bootstrapArgs = staticArgs.map({case t @ Literal(c: Constant) => bootstrapMethodArg(c, t.pos)})
318+
val descriptor = methodBTypeFromMethodType(qual.symbol.info, false)
319+
genLoadArguments(dynamicArgs, qual.symbol.info.params.map(param => typeToBType(param.info)))
320+
mnode.visitInvokeDynamicInsn(qual.symbol.name.encoded, descriptor.descriptor, boostrapDescriptor, bootstrapArgs : _*)
321+
313322
case ApplyDynamic(qual, args) => sys.error("No invokedynamic support yet.")
314323

315324
case This(qual) =>
@@ -1327,7 +1336,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
13271336
val samName = sam.name.toString
13281337
val samMethodType = methodBTypeFromSymbol(sam).toASMType
13291338

1330-
val flags = LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS
1339+
val flags = java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE | java.lang.invoke.LambdaMetafactory.FLAG_MARKERS
13311340

13321341
val ScalaSerializable = classBTypeFromSymbol(definitions.SerializableClass).toASMType
13331342
bc.jmethod.visitInvokeDynamicInsn(samName, invokedType, lambdaMetaFactoryBootstrapHandle,
@@ -1342,16 +1351,4 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
13421351
indyLambdaHosts += cnode.name
13431352
}
13441353
}
1345-
1346-
lazy val lambdaMetaFactoryBootstrapHandle =
1347-
new asm.Handle(asm.Opcodes.H_INVOKESTATIC,
1348-
coreBTypes.jliLambdaMetafactoryRef.internalName, sn.AltMetafactory.toString,
1349-
MethodBType(
1350-
List(
1351-
coreBTypes.jliMethodHandlesLookupRef,
1352-
coreBTypes.StringRef,
1353-
coreBTypes.jliMethodTypeRef,
1354-
ArrayBType(ObjectRef)),
1355-
coreBTypes.jliCallSiteRef
1356-
).descriptor)
13571354
}

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,31 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
130130
*/
131131
final def methodBTypeFromSymbol(methodSymbol: Symbol): MethodBType = {
132132
assert(methodSymbol.isMethod, s"not a method-symbol: $methodSymbol")
133+
methodBTypeFromMethodType(methodSymbol.info, methodSymbol.isClassConstructor || methodSymbol.isConstructor)
134+
}
135+
136+
/**
137+
* Builds a [[MethodBType]] for a method type.
138+
*/
139+
final def methodBTypeFromMethodType(tpe: Type, isConstructor: Boolean): MethodBType = {
133140
val resultType: BType =
134-
if (methodSymbol.isClassConstructor || methodSymbol.isConstructor) UNIT
135-
else typeToBType(methodSymbol.tpe.resultType)
136-
MethodBType(methodSymbol.tpe.paramTypes map typeToBType, resultType)
141+
if (isConstructor) UNIT
142+
else typeToBType(tpe.resultType)
143+
MethodBType(tpe.paramTypes map typeToBType, resultType)
144+
}
145+
146+
def bootstrapMethodArg(t: Constant, pos: Position): AnyRef = t match {
147+
case Constant(mt: Type) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
148+
case c @ Constant(sym: Symbol) => staticHandleFromSymbol(sym)
149+
case c @ Constant(value: String) => value
150+
case c @ Constant(value) if c.isNonUnitAnyVal => c.value.asInstanceOf[AnyRef]
151+
case _ => reporter.error(pos, "Unable to convert static argument of ApplyDynamic into a classfile constant: " + t); null
152+
}
153+
154+
def staticHandleFromSymbol(sym: Symbol): asm.Handle = {
155+
val owner = if (sym.owner.isModuleClass) sym.owner.linkedClassOfClass else sym.owner
156+
val descriptor = methodBTypeFromMethodType(sym.info, isConstructor = false).descriptor
157+
new asm.Handle(asm.Opcodes.H_INVOKESTATIC, classBTypeFromSymbol(owner).internalName, sym.name.encoded, descriptor)
137158
}
138159

139160
/**

src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package scala.tools.nsc
22
package backend.jvm
33

4+
import scala.annotation.switch
5+
import scala.tools.asm
46
import scala.tools.nsc.backend.jvm.BTypes.InternalName
57

68
/**
@@ -111,8 +113,10 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
111113
lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType])
112114
lazy val jliCallSiteRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite])
113115
lazy val jliLambdaMetafactoryRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])
114-
lazy val srLambdaDeserializerRef : ClassBType = classBTypeFromSymbol(requiredModule[scala.runtime.LambdaDeserializer.type].moduleClass)
115116
lazy val srBoxesRunTimeRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])
117+
lazy val srSymbolLiteral : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.SymbolLiteral])
118+
lazy val srStructuralCallSite : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.StructuralCallSite])
119+
lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])
116120
lazy val srBoxedUnitRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxedUnit])
117121

118122
private def methodNameAndType(cls: Symbol, name: Name, static: Boolean = false, filterOverload: Symbol => Boolean = _ => true): MethodNameAndType = {
@@ -265,6 +269,30 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
265269
case _ => false
266270
})
267271
}
272+
273+
lazy val lambdaMetaFactoryBootstrapHandle =
274+
new asm.Handle(asm.Opcodes.H_INVOKESTATIC,
275+
coreBTypes.jliLambdaMetafactoryRef.internalName, sn.AltMetafactory.toString,
276+
MethodBType(
277+
List(
278+
coreBTypes.jliMethodHandlesLookupRef,
279+
coreBTypes.StringRef,
280+
coreBTypes.jliMethodTypeRef,
281+
ArrayBType(ObjectRef)),
282+
coreBTypes.jliCallSiteRef
283+
).descriptor)
284+
285+
lazy val lambdaDeserializeBootstrapHandle =
286+
new scala.tools.asm.Handle(scala.tools.asm.Opcodes.H_INVOKESTATIC,
287+
coreBTypes.srLambdaDeserialize.internalName, sn.Bootstrap.toString,
288+
MethodBType(
289+
List(
290+
coreBTypes.jliMethodHandlesLookupRef,
291+
coreBTypes.StringRef,
292+
coreBTypes.jliMethodTypeRef
293+
),
294+
coreBTypes.jliCallSiteRef
295+
).descriptor)
268296
}
269297

270298
/**
@@ -292,10 +320,10 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
292320
def jiSerializableRef : ClassBType
293321
def juHashMapRef : ClassBType
294322
def juMapRef : ClassBType
323+
def jliCallSiteRef : ClassBType
324+
def jliMethodTypeRef : ClassBType
295325
def jliSerializedLambdaRef : ClassBType
296-
def jliMethodHandlesRef : ClassBType
297326
def jliMethodHandlesLookupRef : ClassBType
298-
def srLambdaDeserializerRef : ClassBType
299327
def srBoxesRunTimeRef : ClassBType
300328
def srBoxedUnitRef : ClassBType
301329

@@ -316,6 +344,9 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
316344
def tupleClassConstructors : Map[InternalName, MethodNameAndType]
317345

318346
def srJFunctionRefs: Set[InternalName]
347+
348+
def lambdaMetaFactoryBootstrapHandle : asm.Handle
349+
def lambdaDeserializeBootstrapHandle : asm.Handle
319350
}
320351

321352
/**
@@ -360,7 +391,6 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
360391
def jliMethodTypeRef : ClassBType = _coreBTypes.jliMethodTypeRef
361392
def jliCallSiteRef : ClassBType = _coreBTypes.jliCallSiteRef
362393
def jliLambdaMetafactoryRef : ClassBType = _coreBTypes.jliLambdaMetafactoryRef
363-
def srLambdaDeserializerRef : ClassBType = _coreBTypes.srLambdaDeserializerRef
364394
def srBoxesRunTimeRef : ClassBType = _coreBTypes.srBoxesRunTimeRef
365395
def srBoxedUnitRef : ClassBType = _coreBTypes.srBoxedUnitRef
366396

@@ -382,6 +412,10 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
382412

383413
def srJFunctionRefs: Set[InternalName] = _coreBTypes.srJFunctionRefs
384414

415+
def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral
416+
def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite
417+
def srLambdaDeserialize : ClassBType = _coreBTypes.srLambdaDeserialize
418+
385419
def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp
386420

387421
// Some symbols. These references should probably be moved to Definitions.
@@ -394,4 +428,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
394428
def BeanInfoAttr: Symbol = _coreBTypes.BeanInfoAttr
395429

396430
def String_valueOf: Symbol = _coreBTypes.String_valueOf
431+
432+
def lambdaMetaFactoryBootstrapHandle = _coreBTypes.lambdaMetaFactoryBootstrapHandle
433+
def lambdaDeserializeBootstrapHandle = _coreBTypes.lambdaDeserializeBootstrapHandle
397434
}

src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,13 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
6464

6565
/**
6666
* Add:
67-
* private static java.util.Map $deserializeLambdaCache$ = null
6867
* private static Object $deserializeLambda$(SerializedLambda l) {
69-
* var cache = $deserializeLambdaCache$
70-
* if (cache eq null) {
71-
* cache = new java.util.HashMap()
72-
* $deserializeLambdaCache$ = cache
73-
* }
74-
* return scala.runtime.LambdaDeserializer.deserializeLambda(MethodHandles.lookup(), cache, l);
68+
* return indy[scala.runtime.LambdaDeserialize.bootstrap](l)
7569
* }
70+
*
71+
* We use invokedynamic here to enable caching within the deserializer without needing to
72+
* host a static field in the enclosing class. This allows us to add this method to interfaces
73+
* that define lambdas in default methods.
7674
*/
7775
def addLambdaDeserialize(classNode: ClassNode): Unit = {
7876
val cw = classNode
@@ -83,37 +81,14 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
8381
// stack map frames and invokes the `getCommonSuperClass` method. This method expects all
8482
// ClassBTypes mentioned in the source code to exist in the map.
8583

86-
val mapDesc = juMapRef.descriptor
8784
val nilLookupDesc = MethodBType(Nil, jliMethodHandlesLookupRef).descriptor
8885
val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor
89-
val lookupMapSerlamObjDesc = MethodBType(jliMethodHandlesLookupRef :: juMapRef :: jliSerializedLambdaRef :: Nil, ObjectRef).descriptor
90-
91-
{
92-
val fv = cw.visitField(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambdaCache$", mapDesc, null, null)
93-
fv.visitEnd()
94-
}
9586

9687
{
9788
val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null)
9889
mv.visitCode()
99-
// javaBinaryName returns the internal name of a class. Also used in BTypesFromsymbols.classBTypeFromSymbol.
100-
mv.visitFieldInsn(GETSTATIC, classNode.name, "$deserializeLambdaCache$", mapDesc)
101-
mv.visitVarInsn(ASTORE, 1)
102-
mv.visitVarInsn(ALOAD, 1)
103-
val l0 = new Label()
104-
mv.visitJumpInsn(IFNONNULL, l0)
105-
mv.visitTypeInsn(NEW, juHashMapRef.internalName)
106-
mv.visitInsn(DUP)
107-
mv.visitMethodInsn(INVOKESPECIAL, juHashMapRef.internalName, "<init>", "()V", false)
108-
mv.visitVarInsn(ASTORE, 1)
109-
mv.visitVarInsn(ALOAD, 1)
110-
mv.visitFieldInsn(PUTSTATIC, classNode.name, "$deserializeLambdaCache$", mapDesc)
111-
mv.visitLabel(l0)
112-
mv.visitFieldInsn(GETSTATIC, srLambdaDeserializerRef.internalName, "MODULE$", srLambdaDeserializerRef.descriptor)
113-
mv.visitMethodInsn(INVOKESTATIC, jliMethodHandlesRef.internalName, "lookup", nilLookupDesc, false)
114-
mv.visitVarInsn(ALOAD, 1)
11590
mv.visitVarInsn(ALOAD, 0)
116-
mv.visitMethodInsn(INVOKEVIRTUAL, srLambdaDeserializerRef.internalName, "deserializeLambda", lookupMapSerlamObjDesc, false)
91+
mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, lambdaDeserializeBootstrapHandle)
11792
mv.visitInsn(ARETURN)
11893
mv.visitEnd()
11994
}

0 commit comments

Comments
 (0)