summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaolo G. Giarrusso <p.giarrusso@gmail.com>2013-03-15 21:42:15 +0100
committerPaolo G. Giarrusso <p.giarrusso@gmail.com>2013-03-16 01:49:30 +0100
commit386a5bd68de3a95563d61b2b305e012c157f3b89 (patch)
treec21628e3d5105b592e2101fa0f484c0e701d7776 /src
parentb7b4f877326acd6a8a24ff60fa1638cc18143c45 (diff)
downloadscala-386a5bd68de3a95563d61b2b305e012c157f3b89.tar.gz
scala-386a5bd68de3a95563d61b2b305e012c157f3b89.tar.bz2
scala-386a5bd68de3a95563d61b2b305e012c157f3b89.zip
SI-7253: respect binary compatibility constraints
From the JLS one can prove that moving a method to a superclass is a binary compatible change, both forward and backward. That's because when compiling a method call `c.foo()`, where c: C, the output descriptor *must* refer to `C` and not to the class where `foo()` is actually defined. This patch just ensures that, and adds a test comparing generated descriptors against the Javac output. The sample code is from Paul Philipps, the fix and the bytecode comparison code from me. From 2006 (9954eafffd5e60676238369ab0ed5797c92b4a7b, a fix for bug 455 in the old bug tracker http://www.scala-lang.org/sites/default/files/aladdin/displayItem.do%3Fid=455.html) until 2.9, Scalac has followed this rule "often" (that is, when C is *not* an interface). This behavior was wrong, but the bug was hard to trigger. AFAICS, this can create problems only when moving a method to a super interface in a library and expecting forward binary compatibility - that is, compiling some Scala client code against the new version of the library, and trying to run this code against the old version of the library. This change grows an interface, so it is valid only if clients are supposed to *not* implement the library. Apparently, this is so rare that nobody noticed. Since 2.10 (0bea2ab5f6b211a83bbf14ea46fe57b8163c6334), Scalac follows this rule *only* when C is an interface (I assume by oversight, since the main change was an accessibility check), so the bug was finally triggered. The new code will have to emit INVOKEINTERFACE instead of INVOKEVIRTUAL a bit more often, compared to 2.9 (but not to 2.10). I don't know whether INVOKEINTERFACE is noticeably slower (it shouldn't be); but this is the safest fix since this behavior is mandated by the JLS. If somebody disagrees and believes the 2.9 is significantly faster, IMHO he should send a separate pull request (although ProGuard is probably a better place for the change).
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala3
2 files changed, 4 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 9b16327ffc..55b20154de 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -2260,6 +2260,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
hostSymbol.info ; methodOwner.info
def isInterfaceCall(sym: Symbol) = (
+ //XXX remove the test for ObjectClass.
sym.isInterface && methodOwner != ObjectClass
|| sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass)
)
@@ -2267,8 +2268,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// the type of the method owner (if not an interface!)
val useMethodOwner = (
style != Dynamic
- || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol)
|| hostSymbol.isBottomClass
+ || methodOwner == ObjectClass
)
val receiver = if (useMethodOwner) methodOwner else hostSymbol
val jowner = javaName(receiver)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 598965b982..ed0d8144ca 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -1197,6 +1197,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
hostSymbol.info ; methodOwner.info
def isInterfaceCall(sym: Symbol) = (
+ //XXX remove the test for ObjectClass.
sym.isInterface && methodOwner != ObjectClass
|| sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass)
)
@@ -1204,8 +1205,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
// the type of the method owner (if not an interface!)
val useMethodOwner = (
style != Dynamic
- || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol)
|| hostSymbol.isBottomClass
+ || methodOwner == ObjectClass
)
val receiver = if (useMethodOwner) methodOwner else hostSymbol
val jowner = javaName(receiver)