summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala12
-rw-r--r--src/partest/scala/tools/partest/javaagent/ASMTransformer.java13
-rw-r--r--src/partest/scala/tools/partest/javaagent/ProfilerVisitor.java13
-rw-r--r--src/partest/scala/tools/partest/javaagent/ProfilingAgent.java2
-rw-r--r--test/files/pos/t6976/Exts_1.scala10
-rw-r--r--test/files/pos/t6976/ImplicitBug_1.scala27
-rw-r--r--test/files/pos/t6976/ImplicitBug_2.scala7
7 files changed, 77 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
index 79e51d5daa..39e16c3f58 100644
--- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
@@ -64,14 +64,18 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
}
}
- /** Return the extension method that corresponds to given instance method `meth`.
- */
+ private def companionModuleForce(sym: Symbol) = {
+ sym.andAlso(_.owner.initialize) // See SI-6976. `companionModule` only calls `rawInfo`. (Why?)
+ sym.companionModule
+ }
+
+ /** Return the extension method that corresponds to given instance method `meth`. */
def extensionMethod(imeth: Symbol): Symbol = atPhase(currentRun.refchecksPhase) {
- val companionInfo = imeth.owner.companionModule.info
+ val companionInfo = companionModuleForce(imeth.owner).info
val candidates = extensionNames(imeth) map (companionInfo.decl(_)) filter (_.exists)
val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe)
assert(matching.nonEmpty,
- s"no extension method found for $imeth:${imeth.tpe} among ${candidates map (c => c.name+":"+c.tpe)} / ${extensionNames(imeth)}")
+ s"no extension method found for $imeth:${imeth.tpe} among ${candidates.map(c => c.name+":"+c.tpe).toList} / ${extensionNames(imeth).toList}")
matching.head
}
diff --git a/src/partest/scala/tools/partest/javaagent/ASMTransformer.java b/src/partest/scala/tools/partest/javaagent/ASMTransformer.java
index 494a5a99be..878c8613d5 100644
--- a/src/partest/scala/tools/partest/javaagent/ASMTransformer.java
+++ b/src/partest/scala/tools/partest/javaagent/ASMTransformer.java
@@ -26,9 +26,18 @@ public class ASMTransformer implements ClassFileTransformer {
className.startsWith("instrumented/"));
}
- public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
+ public byte[] transform(final ClassLoader classLoader, final String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
if (shouldTransform(className)) {
- ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS) {
+ @Override protected String getCommonSuperClass(final String type1, final String type2) {
+ // Since we are not recomputing stack frame map, this should never be called we override this method because
+ // default implementation uses reflection for implementation and might try to load the class that we are
+ // currently processing. That leads to weird results like swallowed exceptions and classes being not
+ // transformed.
+ throw new RuntimeException("Unexpected call to getCommonSuperClass(" + type1 + ", " + type2 +
+ ") while transforming " + className);
+ }
+ };
ProfilerVisitor visitor = new ProfilerVisitor(writer);
ClassReader reader = new ClassReader(classfileBuffer);
reader.accept(visitor, 0);
diff --git a/src/partest/scala/tools/partest/javaagent/ProfilerVisitor.java b/src/partest/scala/tools/partest/javaagent/ProfilerVisitor.java
index ac83f66506..8306327b14 100644
--- a/src/partest/scala/tools/partest/javaagent/ProfilerVisitor.java
+++ b/src/partest/scala/tools/partest/javaagent/ProfilerVisitor.java
@@ -33,6 +33,19 @@ public class ProfilerVisitor extends ClassVisitor implements Opcodes {
// only instrument non-abstract methods
if((access & ACC_ABSTRACT) == 0) {
assert(className != null);
+ /* The following instructions do not modify compressed stack frame map so
+ * we don't need to worry about recalculating stack frame map. Specifically,
+ * let's quote "ASM 4.0, A Java bytecode engineering library" guide (p. 40):
+ *
+ * In order to save space, a compiled method does not contain one frame per
+ * instruction: in fact it contains only the frames for the instructions
+ * that correspond to jump targets or exception handlers, or that follow
+ * unconditional jump instructions. Indeed the other frames can be easily
+ * and quickly inferred from these ones.
+ *
+ * Instructions below are just loading constants and calling a method so according
+ * to definition above they do not contribute to compressed stack frame map.
+ */
mv.visitLdcInsn(className);
mv.visitLdcInsn(name);
mv.visitLdcInsn(desc);
diff --git a/src/partest/scala/tools/partest/javaagent/ProfilingAgent.java b/src/partest/scala/tools/partest/javaagent/ProfilingAgent.java
index c2e4dc69f4..3b18987040 100644
--- a/src/partest/scala/tools/partest/javaagent/ProfilingAgent.java
+++ b/src/partest/scala/tools/partest/javaagent/ProfilingAgent.java
@@ -20,6 +20,6 @@ public class ProfilingAgent {
// and the test-case itself won't be loaded yet. We rely here on the fact that ASMTransformer does
// not depend on Scala library. In case our assumptions are wrong we can always insert call to
// inst.retransformClasses.
- inst.addTransformer(new ASMTransformer(), true);
+ inst.addTransformer(new ASMTransformer(), false);
}
}
diff --git a/test/files/pos/t6976/Exts_1.scala b/test/files/pos/t6976/Exts_1.scala
new file mode 100644
index 0000000000..9b3a69edd9
--- /dev/null
+++ b/test/files/pos/t6976/Exts_1.scala
@@ -0,0 +1,10 @@
+object Exts {
+ implicit class AnyExts[T](val o: T) extends AnyVal {
+ def moo = "moo!"
+ }
+}
+
+trait Exts {
+ import language.implicitConversions
+ implicit def AnyExts[T](o: T) = Exts.AnyExts(o)
+}
diff --git a/test/files/pos/t6976/ImplicitBug_1.scala b/test/files/pos/t6976/ImplicitBug_1.scala
new file mode 100644
index 0000000000..c9031bab2e
--- /dev/null
+++ b/test/files/pos/t6976/ImplicitBug_1.scala
@@ -0,0 +1,27 @@
+// This one is weird and nasty. Not sure if this is scalac or sbt
+// (tried with 0.12 & 0.12.2-RC2) bug.
+//
+// A level of indirection is required to trigger this bug.
+// Exts seems to need to be defined in separate file.
+//
+// Steps to reproduce:
+// 1. sbt clean
+// 2. sbt run (it works)
+// 3. Comment A & uncomment B.
+// 4. sbt run (it fails)
+// 5. Switch it back & sbt run. It still fails.
+//
+// In this project sbt clean helps. However in a large project where this
+// bug was found compiler crashed even after doing sbt clean. The only
+// way to work around this was to reference Exts object explicitly (C) in
+// the source file using its implicit classes.
+
+// Lets suppose this is a mega-trait combining all sorts of helper
+// functionality.
+trait Support extends Exts
+
+object ImplicitsBug extends App with Support { // A
+// object ImplicitsBug extends App with Exts { // B
+ //Exts // C) this reference helped in the large project.
+ println(3.moo)
+}
diff --git a/test/files/pos/t6976/ImplicitBug_2.scala b/test/files/pos/t6976/ImplicitBug_2.scala
new file mode 100644
index 0000000000..2fea5e2993
--- /dev/null
+++ b/test/files/pos/t6976/ImplicitBug_2.scala
@@ -0,0 +1,7 @@
+trait Support extends Exts
+
+// object ImplicitsBug extends App with Support { // A
+object ImplicitsBug extends App with Exts { // B
+ //Exts // C) this reference helped in the large project.
+ println(3.moo)
+}