@@ -88,6 +88,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
88
88
case class DelambdafyAnonClass (lambdaClassDef : ClassDef , newExpr : Tree ) extends TransformedFunction
89
89
case class InvokeDynamicLambda (tree : Apply ) extends TransformedFunction
90
90
91
+ private val boxingBridgeMethods = mutable.ArrayBuffer [Tree ]()
92
+
91
93
// here's the main entry point of the transform
92
94
override def transform (tree : Tree ): Tree = tree match {
93
95
// the main thing we care about is lambdas
@@ -105,6 +107,12 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
105
107
// ... or an invokedynamic call
106
108
super .transform(apply)
107
109
}
110
+ case Template (_, _, _) =>
111
+ try {
112
+ // during this call boxingBridgeMethods will be populated from the Function case
113
+ val Template (parents, self, body) = super .transform(tree)
114
+ Template (parents, self, body ++ boxingBridgeMethods)
115
+ } finally boxingBridgeMethods.clear()
108
116
case _ => super .transform(tree)
109
117
}
110
118
@@ -137,6 +145,61 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
137
145
138
146
val isStatic = target.hasFlag(STATIC )
139
147
148
+ def createBoxingBridgeMethod (functionParamTypes : List [Type ], functionResultType : Type ): Tree = {
149
+ val methSym = oldClass.newMethod(target.name.append(" $adapted" ).toTermName, target.pos, target.flags | FINAL | ARTIFACT )
150
+ var neededAdaptation = false
151
+ def boxedType (tpe : Type ): Type = {
152
+ if (isPrimitiveValueClass(tpe.typeSymbol)) {neededAdaptation = true ; ObjectTpe }
153
+ else if (enteringErasure(tpe.typeSymbol.isDerivedValueClass)) {neededAdaptation = true ; ObjectTpe }
154
+ else tpe
155
+ }
156
+ val targetParams : List [Symbol ] = target.paramss.head
157
+ val numCaptures = targetParams.length - functionParamTypes.length
158
+ val (targetCaptureParams, targetFunctionParams) = targetParams.splitAt(numCaptures)
159
+ val bridgeParams : List [Symbol ] =
160
+ targetCaptureParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) :::
161
+ map2(targetFunctionParams, functionParamTypes)((param, tp) => methSym.newSyntheticValueParam(boxedType(tp), param.name.toTermName))
162
+
163
+ val bridgeResultType : Type = {
164
+ if (target.info.resultType == UnitTpe && functionResultType != UnitTpe ) {
165
+ neededAdaptation = true
166
+ ObjectTpe
167
+ } else
168
+ boxedType(functionResultType)
169
+ }
170
+ val methodType = MethodType (bridgeParams, bridgeResultType)
171
+ methSym setInfo methodType
172
+ if (! neededAdaptation)
173
+ EmptyTree
174
+ else {
175
+ val bridgeParamTrees = bridgeParams.map(ValDef (_))
176
+
177
+ oldClass.info.decls enter methSym
178
+
179
+ val body = localTyper.typedPos(originalFunction.pos) {
180
+ val newTarget = Select (gen.mkAttributedThis(oldClass), target)
181
+ val args : List [Tree ] = mapWithIndex(bridgeParams) { (param, i) =>
182
+ if (i < numCaptures) {
183
+ gen.mkAttributedRef(param)
184
+ } else {
185
+ val functionParam = functionParamTypes(i - numCaptures)
186
+ val targetParam = targetParams(i)
187
+ if (enteringErasure(functionParam.typeSymbol.isDerivedValueClass)) {
188
+ val casted = cast(gen.mkAttributedRef(param), functionParam)
189
+ val unboxed = unbox(casted, ErasedValueType (functionParam.typeSymbol, targetParam.tpe)).modifyType(postErasure.elimErasedValueType)
190
+ unboxed
191
+ } else adaptToType(gen.mkAttributedRef(param), targetParam.tpe)
192
+ }
193
+ }
194
+ gen.mkMethodCall(newTarget, args)
195
+ }
196
+ val body1 = if (enteringErasure(functionResultType.typeSymbol.isDerivedValueClass))
197
+ adaptToType(box(body.setType(ErasedValueType (functionResultType.typeSymbol, body.tpe)), " boxing lambda target" ), bridgeResultType)
198
+ else adaptToType(body, bridgeResultType)
199
+ val methDef0 = DefDef (methSym, List (bridgeParamTrees), body1)
200
+ postErasure.newTransformer(unit).transform(methDef0).asInstanceOf [DefDef ]
201
+ }
202
+ }
140
203
/**
141
204
* Creates the apply method for the anonymous subclass of FunctionN
142
205
*/
@@ -292,22 +355,56 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
292
355
thisArg ::: captureArgs
293
356
}
294
357
295
- val functionalInterface = java8CompatFunctionalInterface(target, originalFunction.tpe)
358
+ val arity = originalFunction.vparams.length
359
+
360
+ // Reconstruct the type of the function entering erasure.
361
+ // We do this by taking the type after erasure, and re-boxing `ErasedValueType`.
362
+ //
363
+ // Unfortunately, the more obvious `enteringErasure(target.info)` doesn't work
364
+ // as we would like, value classes in parameter position show up as the unboxed types.
365
+ val (functionParamTypes, functionResultType) = exitingErasure {
366
+ def boxed (tp : Type ) = tp match {
367
+ case ErasedValueType (valueClazz, _) => TypeRef (NoPrefix , valueClazz, Nil )
368
+ case _ => tp
369
+ }
370
+ // We don't need to deeply map `boxedValueClassType` over the infos as `ErasedValueType`
371
+ // will only appear directly as a parameter type in a method signature, as shown
372
+ // https://gist.github.com/retronym/ba81dbd462282c504ff8
373
+ val info = target.info
374
+ val boxedParamTypes = info.paramTypes.takeRight(arity).map(boxed)
375
+ (boxedParamTypes, boxed(info.resultType))
376
+ }
377
+ val functionType = definitions.functionType(functionParamTypes, functionResultType)
378
+
379
+ val (functionalInterface, isSpecialized) = java8CompatFunctionalInterface(target, functionType)
296
380
if (functionalInterface.exists) {
297
381
// Create a symbol representing a fictional lambda factory method that accepts the captured
298
382
// arguments and returns a Function.
299
- val msym = currentOwner.newMethod(nme.ANON_FUN_NAME , originalFunction.pos, ARTIFACT )
383
+ val msym = currentOwner.newMethod(nme.ANON_FUN_NAME , originalFunction.pos, ARTIFACT )
300
384
val argTypes : List [Type ] = allCaptureArgs.map(_.tpe)
301
385
val params = msym.newSyntheticValueParams(argTypes)
302
- msym.setInfo(MethodType (params, originalFunction.tpe ))
386
+ msym.setInfo(MethodType (params, functionType ))
303
387
val arity = originalFunction.vparams.length
304
388
389
+ val lambdaTarget =
390
+ if (isSpecialized)
391
+ target
392
+ else {
393
+ createBoxingBridgeMethod(functionParamTypes, functionResultType) match {
394
+ case EmptyTree =>
395
+ target
396
+ case bridge =>
397
+ boxingBridgeMethods += bridge
398
+ bridge.symbol
399
+ }
400
+ }
401
+
305
402
// We then apply this symbol to the captures.
306
403
val apply = localTyper.typedPos(originalFunction.pos)(Apply (Ident (msym), allCaptureArgs)).asInstanceOf [Apply ]
307
404
308
405
// The backend needs to know the target of the lambda and the functional interface in order
309
406
// to emit the invokedynamic instruction. We pass this information as tree attachment.
310
- apply.updateAttachment(LambdaMetaFactoryCapable (target , arity, functionalInterface))
407
+ apply.updateAttachment(LambdaMetaFactoryCapable (lambdaTarget , arity, functionalInterface))
311
408
InvokeDynamicLambda (apply)
312
409
} else {
313
410
val anonymousClassDef = makeAnonymousClass
@@ -469,34 +566,24 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
469
566
final case class LambdaMetaFactoryCapable (target : Symbol , arity : Int , functionalInterface : Symbol )
470
567
471
568
// The functional interface that can be used to adapt the lambda target method `target` to the
472
- // given function type. Returns `NoSymbol` if the compiler settings are unsuitable, or `LambdaMetaFactory`
473
- // would be unable to generate the correct implementation (e.g. functions referring to derived value classes)
474
- private def java8CompatFunctionalInterface (target : Symbol , functionType : Type ): Symbol = {
569
+ // given function type. Returns `NoSymbol` if the compiler settings are unsuitable.
570
+ private def java8CompatFunctionalInterface (target : Symbol , functionType : Type ): (Symbol , Boolean ) = {
475
571
val canUseLambdaMetafactory : Boolean = {
476
- val hasValueClass = exitingErasure {
477
- val methodType : Type = target.info
478
- methodType.exists(_.isInstanceOf [ErasedValueType ])
479
- }
480
572
val isTarget18 = settings.target.value.contains(" jvm-1.8" )
481
- settings.isBCodeActive && isTarget18 && ! hasValueClass
573
+ settings.isBCodeActive && isTarget18
482
574
}
483
575
484
- def functionalInterface : Symbol = {
485
- val sym = functionType.typeSymbol
486
- val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage
487
- val name1 = specializeTypes.specializedFunctionName(sym, functionType.typeArgs)
488
- val paramTps :+ restpe = functionType.typeArgs
489
- val arity = paramTps.length
490
- if (name1.toTypeName == sym.name) {
491
- val returnUnit = restpe.typeSymbol == UnitClass
492
- val functionInterfaceArray =
493
- if (returnUnit) currentRun.runDefinitions.Scala_Java8_CompatPackage_JProcedure
494
- else currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction
495
- functionInterfaceArray.apply(arity)
496
- } else {
497
- pack.info.decl(name1.toTypeName.prepend(" J" ))
498
- }
576
+ val sym = functionType.typeSymbol
577
+ val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage
578
+ val name1 = specializeTypes.specializedFunctionName(sym, functionType.typeArgs)
579
+ val paramTps :+ restpe = functionType.typeArgs
580
+ val arity = paramTps.length
581
+ val isSpecialized = name1.toTypeName != sym.name
582
+ val functionalInterface = if (! isSpecialized) {
583
+ currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction (arity)
584
+ } else {
585
+ pack.info.decl(name1.toTypeName.prepend(" J" ))
499
586
}
500
- if (canUseLambdaMetafactory) functionalInterface else NoSymbol
587
+ ( if (canUseLambdaMetafactory) functionalInterface else NoSymbol , isSpecialized)
501
588
}
502
589
}
0 commit comments