Skip to content

Prototype indy+unreflectSpecial for super calls #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1074,16 +1074,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
import InvokeStyle._
if (style == Super) {
assert(receiverClass == methodOwner, s"for super call, expecting $receiverClass == $methodOwner")
if (receiverClass.isTrait && !receiverClass.isJavaDefined) {
val staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor
val staticName = traitImplMethodName(method).toString
bc.invokestatic(receiverName, staticName, staticDesc, isInterface, pos)
} else {
if (receiverClass.isTraitOrInterface) {
// An earlier check in Mixin reports an error in this case, so it doesn't reach the backend
assert(cnode.interfaces.contains(receiverName), s"cannot invokespecial $receiverName.$jname, the interface is not a direct parent.")
}
if (method.isClassConstructor) {
bc.invokespecial(receiverName, jname, mdescr, isInterface, pos)
} else if (method.isMixinConstructor) {
val staticName = method.javaSimpleName.toString
bc.invokestatic(receiverName, staticName, mdescr, isInterface, pos)
} else {
def staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor
bc.jmethod.visitInvokeDynamicInsn(jname, staticDesc, coreBTypes.invokeExactBootstrapHandle, classBTypeFromSymbol(methodOwner).toASMType)
}
} else {
val opc = style match {
Expand Down
8 changes: 0 additions & 8 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}
}

def needsStaticImplMethod(sym: Symbol) = sym.hasAttachment[global.mixer.NeedStaticImpl.type]

final def traitImplMethodName(sym: Symbol): Name = {
val name = sym.javaSimpleName
if (sym.isMixinConstructor) name
else name.append(nme.NAME_JOIN_STRING)
}

/**
* True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
* member class. This method is used to decide if we should emit an EnclosingMethod attribute.
Expand Down
15 changes: 2 additions & 13 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -490,19 +490,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {

case dd : DefDef =>
val sym = dd.symbol
if (needsStaticImplMethod(sym)) {
val staticDefDef = global.gen.mkStatic(dd, traitImplMethodName(sym), _.cloneSymbol)
val forwarderDefDef = {
val forwarderBody = Apply(global.gen.mkAttributedRef(staticDefDef.symbol), This(sym.owner).setType(sym.owner.typeConstructor) :: dd.vparamss.head.map(p => global.gen.mkAttributedIdent(p.symbol))).setType(sym.info.resultType)
// we don't want to the optimizer to inline the static method into the forwarder. Instead,
// the backend has a special case to transitively inline into a callsite of the forwarder
// when the forwarder itself is inlined.
forwarderBody.updateAttachment(NoInlineCallsiteAttachment)
deriveDefDef(dd)(_ => global.atPos(dd.pos)(forwarderBody))
}
genDefDef(staticDefDef)
if (!sym.isMixinConstructor)
genDefDef(forwarderDefDef)
if (sym.isMixinConstructor) {
genDefDef(global.gen.mkStatic(dd, sym.javaSimpleName, sym => sym))
} else genDefDef(dd)

case Template(_, _, body) => body foreach gen
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import scala.collection.{concurrent, mutable}
import scala.collection.concurrent.TrieMap
import scala.reflect.internal.util.Position
import scala.tools.asm
import asm.Opcodes
import asm.{Opcodes, Type}
import scala.tools.asm.tree._
import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo}
import scala.tools.nsc.backend.jvm.BackendReporting._
Expand Down Expand Up @@ -1093,7 +1093,9 @@ abstract class BTypes {
}
}

final case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType
final case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType {
def toAsmType: Type = asm.Type.getMethodType(returnType.toASMType, argumentTypes.map(_.toASMType): _*)
}

/* Some definitions that are required for the implementation of BTypes. They are abstract because
* initializing them requires information from types / symbols, which is not accessible here in
Expand Down
18 changes: 1 addition & 17 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -584,23 +584,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass))

if (needsStaticImplMethod(methodSym)) {
val staticName = traitImplMethodName(methodSym).toString
val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF)
val staticMethodType = methodSym.info match {
case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res)
}
val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false)
val staticMethodInfo = MethodInlineInfo(
effectivelyFinal = true,
annotatedInline = info.annotatedInline,
annotatedNoInline = info.annotatedNoInline)
if (methodSym.isMixinConstructor)
List((staticMethodSignature, staticMethodInfo))
else
List((signature, info), (staticMethodSignature, staticMethodInfo))
} else
List((signature, info))
List((signature, info))
}
}).toMap

Expand Down
19 changes: 19 additions & 0 deletions src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable
lazy val jiSerializableRef : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable
lazy val jlClassCastExceptionRef : ClassBType = classBTypeFromSymbol(ClassCastExceptionClass) // java/lang/ClassCastException
lazy val jlClassRef : ClassBType = classBTypeFromSymbol(ClassClass) // java/lang/Class
lazy val juMapRef : ClassBType = classBTypeFromSymbol(JavaUtilMap) // java/util/Map
lazy val juHashMapRef : ClassBType = classBTypeFromSymbol(JavaUtilHashMap) // java/util/HashMap
lazy val sbScalaBeanInfoRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.beans.ScalaBeanInfo])
Expand All @@ -117,6 +118,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
lazy val srSymbolLiteral : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.SymbolLiteral])
lazy val srStructuralCallSite : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.StructuralCallSite])
lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])
lazy val srInvokeExact : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.InvokeExact])
lazy val srBoxedUnitRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxedUnit])

private def methodNameAndType(cls: Symbol, name: Name, static: Boolean = false, filterOverload: Symbol => Boolean = _ => true): MethodNameAndType = {
Expand Down Expand Up @@ -288,6 +290,20 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
coreBTypes.jliCallSiteRef
).descriptor,
/* itf = */ coreBTypes.srLambdaDeserialize.isInterface.get)

lazy val invokeExactBootstrapHandle =
new scala.tools.asm.Handle(scala.tools.asm.Opcodes.H_INVOKESTATIC,
coreBTypes.srInvokeExact.internalName, sn.Bootstrap.toString,
MethodBType(
List(
coreBTypes.jliMethodHandlesLookupRef,
coreBTypes.StringRef,
coreBTypes.jliMethodTypeRef,
coreBTypes.jlClassRef
),
coreBTypes.jliCallSiteRef
).descriptor,
/* itf = */ coreBTypes.srInvokeExact.isInterface.get)
}

/**
Expand Down Expand Up @@ -377,6 +393,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def jlThrowableRef : ClassBType = _coreBTypes.jlThrowableRef
def jlCloneableRef : ClassBType = _coreBTypes.jlCloneableRef
def jiSerializableRef : ClassBType = _coreBTypes.jiSerializableRef
def jlClassRef : ClassBType = _coreBTypes.jlClassRef
def jlClassCastExceptionRef : ClassBType = _coreBTypes.jlClassCastExceptionRef
def juMapRef : ClassBType = _coreBTypes.juMapRef
def juHashMapRef : ClassBType = _coreBTypes.juHashMapRef
Expand Down Expand Up @@ -410,6 +427,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral
def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite
def srLambdaDeserialize : ClassBType = _coreBTypes.srLambdaDeserialize
def srInvokeExact : ClassBType = _coreBTypes.srInvokeExact

def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp

Expand All @@ -427,4 +445,5 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def lambdaMetaFactoryMetafactoryHandle : asm.Handle = _coreBTypes.lambdaMetaFactoryMetafactoryHandle
def lambdaMetaFactoryAltMetafactoryHandle : asm.Handle = _coreBTypes.lambdaMetaFactoryAltMetafactoryHandle
def lambdaDeserializeBootstrapHandle : asm.Handle = _coreBTypes.lambdaDeserializeBootstrapHandle
def invokeExactBootstrapHandle : asm.Handle = _coreBTypes.invokeExactBootstrapHandle
}
10 changes: 6 additions & 4 deletions src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ package scala.tools.nsc
package backend.jvm
package opt

import scala.annotation.{tailrec, switch}

import scala.annotation.{switch, tailrec}
import scala.tools.asm.Type
import scala.tools.asm.tree.analysis.Frame
import scala.tools.asm.tree.analysis.{AnalyzerException, Frame}
import scala.tools.asm.Opcodes._
import scala.tools.asm.tree._
import scala.collection.mutable
Expand Down Expand Up @@ -201,7 +200,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
*
* Returns `true` if the bytecode of `method` was changed.
*/
def methodOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = {
def methodOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = try {
if (method.instructions.size == 0) return false // fast path for abstract methods

// unreachable-code also removes unused local variable nodes and empty exception handlers.
Expand Down Expand Up @@ -383,6 +382,9 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
assert(nullOrEmpty(method.invisibleLocalVariableAnnotations), method.invisibleLocalVariableAnnotations)

nullnessDceBoxesCastsCopypropPushpopOrJumpsChanged || localsRemoved || lineNumbersRemoved || labelsRemoved
} catch {
case ex: AnalyzerException =>
throw new AnalyzerException(ex.node, s"${ex.getMessage} + While optimizing: ${AsmUtils.textify(method)}", ex.getCause)
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/compiler/scala/tools/nsc/transform/Delambdafy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// delambdafy targets are excluded as they are made static by `transformFunction`.
if (!dd.symbol.hasFlag(STATIC) && !methodReferencesThis(dd.symbol)) {
dd.symbol.setFlag(STATIC)
dd.symbol.removeAttachment[mixer.NeedStaticImpl.type]
}
super.transform(tree)
case Apply(fun, outer :: rest) if shouldElideOuterArg(fun.symbol, outer) =>
Expand Down
19 changes: 0 additions & 19 deletions src/compiler/scala/tools/nsc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
*/
def addMember(clazz: Symbol, member: Symbol): Symbol = {
debuglog(s"mixing into $clazz: ${member.defString}")
// This attachment is used to instruct the backend about which methids in traits require
// a static trait impl method. We remove this from the new symbol created for the method
// mixed into the subclass.
member.removeAttachment[NeedStaticImpl.type]
clazz.info.decls enter member setFlag MIXEDIN resetFlag JAVA_DEFAULTMETHOD
}
def cloneAndAddMember(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol =
Expand Down Expand Up @@ -1017,11 +1013,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
// add all new definitions to current class or interface
val body1 = addNewDefs(currentOwner, bodyEmptyAccessors)
body1 foreach {
case dd: DefDef if isTraitMethodRequiringStaticImpl(dd) =>
dd.symbol.updateAttachment(NeedStaticImpl)
case _ =>
}
treeCopy.Template(tree, parents1, self, body1)

case Select(qual, name) if sym.owner.isTrait && !sym.isMethod =>
Expand Down Expand Up @@ -1056,14 +1047,4 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
finally localTyper = saved
}
}

private def isTraitMethodRequiringStaticImpl(dd: DefDef): Boolean = {
val sym = dd.symbol
dd.rhs.nonEmpty &&
sym.owner.isTrait &&
!sym.isPrivate && // no need to put implementations of private methods into a static method
!sym.hasFlag(Flags.STATIC)
}

case object NeedStaticImpl extends PlainAttachment
}
25 changes: 25 additions & 0 deletions src/library/scala/runtime/InvokeExact.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package scala.runtime;

import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class InvokeExact {
private static MethodHandles.Lookup TRUSTED = getTrustedLookup();

public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName, MethodType invokedType,
Class<?> fromSite) throws Throwable {
Method method = fromSite.getMethod(invokedName, invokedType.dropParameterTypes(0, 1).parameterArray());
return new ConstantCallSite(TRUSTED.unreflectSpecial(method, fromSite));
}

private static MethodHandles.Lookup getTrustedLookup() {
try {
Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
field.setAccessible(true);
return (MethodHandles.Lookup) field.get(null);
} catch (ReflectiveOperationException e) {
return null;
}
}
}
19 changes: 19 additions & 0 deletions test/files/run/sd143.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class A {
def m = 1
}

class B extends A {
override def m = 2
}

trait T extends A

class C extends B with T {
override def m = super[T].m
}

object Test {
def main(args: Array[String]): Unit = {
println((new C).m)
}
}