diff options
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) +} |