diff --git a/retrolambda/pom.xml b/retrolambda/pom.xml
index c25f5cf6..8a6dda98 100644
--- a/retrolambda/pom.xml
+++ b/retrolambda/pom.xml
@@ -14,6 +14,11 @@
jar
+
+ org.scala-lang
+ scala-library
+ 2.11.2
+
org.ow2.asm
@@ -70,7 +75,21 @@
-
+
+ net.alchim31.maven
+ scala-maven-plugin
+ 3.2.0
+
+
+ scala-compile-first
+ process-resources
+
+ add-source
+ compile
+
+
+
+
diff --git a/retrolambda/src/main/scala/AsmTest.scala b/retrolambda/src/main/scala/AsmTest.scala
new file mode 100644
index 00000000..73b1c875
--- /dev/null
+++ b/retrolambda/src/main/scala/AsmTest.scala
@@ -0,0 +1,66 @@
+/**
+ * Created by arneball on 2014-07-29.
+ */
+
+import java.lang.reflect.{Method, Modifier}
+import java.net.{URL, URLClassLoader}
+import java.nio.file._
+import java.nio.file.attribute.BasicFileAttributes
+import org.objectweb.asm._
+import collection.JavaConversions._
+
+object AsmTest {
+ lazy val (output: Path, cp: Array[URL], input: Path, bytecodeVersion: Int, ucl: URLClassLoader) = {
+ val List(_input, _output, _cp, _bytecode) = List("inputDir", "outputDir", "classpath", "bytecodeVersion").map{ name =>
+ System.getProperties.getProperty("retrometh." + name)
+ }
+ val byteCodeVersion = _bytecode match {
+ case "1.7" | "7" | "51" => Opcodes.V1_7
+ case "1.6" | "6" | "50" => Opcodes.V1_6
+ case "1.5" | "5" | "49" => Opcodes.V1_5
+ case _ => throw new Exception("BytecodeVersion must be 1.6, 6, 1.7 or 7")
+ }
+ val input = Paths.get(_input)
+ val output = Option(_output).map{ Paths.get(_) }.getOrElse(input)
+
+ val cp = _cp.split(System.getProperty("path.separator")).map{ p =>
+ Paths.get(p).toUri.toURL
+ } :+ input.toUri.toURL
+ Implicits.println(s"Input: $input, Output: $output, classpath: ${cp.mkString}")
+ (output, cp, input, byteCodeVersion, new URLClassLoader(cp))
+ }
+
+ def main(args: Array[String]): Unit = {
+ Files.walkFileTree(input, new SimpleFileVisitor[Path] {
+ override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = file.toString.toLowerCase.endsWith("class") match {
+ case true =>
+ println("Found class file " + file)
+ val bytecode = Files.readAllBytes(file)
+ val wr = new ClassWriter(ClassWriter.COMPUTE_FRAMES)
+ val stage1 = new InterfaceModifier(wr, bytecodeVersion)
+ val stage2 = new ClassModifier(stage1, bytecodeVersion)
+ val reader = new ClassReader(bytecode).accept(stage2, 0);
+ val outputFile = output.resolve(input.relativize(file));
+ Files.createDirectories(outputFile.getParent());
+ Files.write(outputFile, wr.toByteArray);
+ FileVisitResult.CONTINUE
+ case that =>
+ FileVisitResult.CONTINUE
+ }
+ })
+ }
+}
+
+object Implicits {
+ def println(str: String) = Predef.println("KALLE " + str)
+ val Sig = raw"\((.*)\)(.*)".r
+ implicit class StrWr(val str: String) extends AnyVal {
+ def addParam(cl: String) = str match {
+ case Sig(content, returntype) =>
+ val tmp = s"(L${cl};$content)$returntype"
+ println(s"Before $str, now $tmp")
+ tmp
+ }
+ def getInternalClass = AsmTest.ucl.loadClass(str.replace("/", "."))
+ }
+}
\ No newline at end of file
diff --git a/retrolambda/src/main/scala/ClassModifier.scala b/retrolambda/src/main/scala/ClassModifier.scala
new file mode 100644
index 00000000..3b1e429d
--- /dev/null
+++ b/retrolambda/src/main/scala/ClassModifier.scala
@@ -0,0 +1,91 @@
+import java.lang.reflect.{Method, Modifier}
+
+import Implicits._
+import org.objectweb.asm.Opcodes._
+import org.objectweb.asm.Type._
+import org.objectweb.asm._
+
+import scala.collection.mutable.ArrayBuffer
+
+case class MethodContainer(methodName: String, methodDesc: String, interface: String, signature: String)
+class ClassModifier(visitor: ClassVisitor, byteCodeVersion: Int = V1_6) extends ClassVisitor(ASM5, visitor) {
+
+ var methodsToOverride: List[MethodContainer] = Nil
+ var className: String = _
+ type MethodDesc = (String, String)
+ var implementedMethods = ArrayBuffer[MethodDesc]()
+
+ override def visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array[String]) = {
+ val isClass = (access & ACC_INTERFACE) == 0
+ if(isClass) {
+ val defaultMethods = for {
+ interface <- interfaces
+ cl = interface.getInternalClass
+ m @ DefaultMethod() <- cl.getMethods
+ } yield MethodContainer(m.getName, Type.getMethodDescriptor(m), interface, signature)
+ println(s"Default methods: ${defaultMethods.mkString(",")}")
+ methodsToOverride = defaultMethods.toList
+ className = name
+ }
+ super.visit(byteCodeVersion, access, name, signature, superName, interfaces)
+ }
+
+ // gotta keep track of overriden default methods
+ override def visitMethod(access: Int, name: String, desc: String, signature: String, exceptions: Array[String]): MethodVisitor = {
+ implementedMethods += name -> desc
+ new InterfaceToHelperRewriter(super.visitMethod(access, name, desc, signature, exceptions))
+ }
+
+ override def visitEnd() = {
+ def createProxy(l: MethodContainer) = l match {
+ case MethodContainer(name, desc, interface, signature) if !implementedMethods.contains(name -> desc) =>
+ val tmp = super.visitMethod(ACC_PUBLIC, name, desc, signature, null)
+ tmp.visitVarInsn(ALOAD, 0)
+ Type.getArgumentTypes(desc).zipWithIndex.foreach{
+ case (PrimitiveLoad(instruction), i) => tmp.visitVarInsn(instruction, i + 1)
+ case (_, i) => tmp.visitVarInsn(ALOAD, i + 1)
+ }
+ tmp.visitMethodInsn(INVOKESTATIC, interface + "helper", name, desc.addParam(interface), false)
+ Type.getReturnType(desc) match {
+ case ReturnIns(ins) => tmp.visitInsn(ins)
+ case otherwise: Type => tmp.visitInsn(ARETURN)
+ }
+ tmp.visitMaxs(0, 0)
+ tmp.visitEnd()
+ case _ => // noop
+ }
+ methodsToOverride.foreach{ createProxy }
+ super.visitEnd()
+ }
+}
+
+class InterfaceToHelperRewriter(mv: MethodVisitor) extends MethodVisitor(Opcodes.ASM5, mv) {
+ override def visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, isInterface: Boolean) = opcode match {
+ case INVOKESPECIAL if isInterface =>
+ super.visitMethodInsn(INVOKESTATIC, owner + "helper", name, desc.addParam(owner), false)
+ case INVOKESTATIC if isInterface =>
+ super.visitMethodInsn(INVOKESTATIC, owner + "helper", name + "$static", desc, false)
+ case _ =>
+ super.visitMethodInsn(opcode, owner, name, desc, isInterface)
+ }
+}
+
+object DefaultMethod {
+ def unapply(m: Method) = !Modifier.isAbstract(m.getModifiers)
+}
+
+object InstructorExtractor {
+ val (loadMap, returnMap) = {
+ val types = List(INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE, VOID_TYPE)
+ val loads = List(ILOAD, LLOAD, FLOAD, DLOAD, -1) // omg a null
+ val rets = List(IRETURN, LRETURN, FRETURN, DRETURN, RETURN)
+ val mkMap = types.zip(_: List[Int]).toMap
+ mkMap(loads) -> mkMap(rets)
+ }
+}
+
+class InstructorExtractor(m: Map[Type, Int]) {
+ def unapply(f: Type) = m.get(f)
+}
+object PrimitiveLoad extends InstructorExtractor(InstructorExtractor.loadMap)
+object ReturnIns extends InstructorExtractor(InstructorExtractor.returnMap)
\ No newline at end of file
diff --git a/retrolambda/src/main/scala/InterfaceModifier.scala b/retrolambda/src/main/scala/InterfaceModifier.scala
new file mode 100644
index 00000000..20bf92e7
--- /dev/null
+++ b/retrolambda/src/main/scala/InterfaceModifier.scala
@@ -0,0 +1,75 @@
+import java.nio.file.Files
+
+import Implicits._
+import org.objectweb.asm.Opcodes._
+import org.objectweb.asm._
+
+import scala.collection.{mutable => m}
+
+class InterfaceModifier(classWriter: ClassWriter, targetByteCode: Int = Opcodes.V1_6) extends ClassVisitor(ASM5, classWriter) with Opcodes {
+ private var cName: String = _
+ private var isInterface = false
+ private lazy val helperClassVisitor = mkHelperClass
+ private lazy val helperClassName = cName + "helper"
+
+ override def visitMethod(access: Int, name: String, desc: String, signature: String, exceptions: Array[String]): MethodVisitor = {
+ val methConcrete = (ACC_ABSTRACT & access) == 0
+ val isStatic = (ACC_STATIC & access) != 0
+ (methConcrete, isInterface, isStatic) match {
+ case (true, true, false) => // concrete interface method
+ super.visitMethod(access | ACC_ABSTRACT, name, desc, signature, exceptions)
+ val tmp = helperClassVisitor.visitMethod(access | ACC_STATIC, name, desc.addParam(cName), signature, exceptions)
+ new BodyStripper(tmp)
+ case (true, true, true) => // static interface method
+ helperClassVisitor.visitMethod(access, name + "$static", desc, signature, exceptions)
+ case _ =>
+ super.visitMethod(access, name, desc, signature, exceptions)
+ }
+ }
+
+ override def visitEnd() = {
+ val newPath = AsmTest.output.resolve(helperClassName + ".class")
+ println("CREATING HELPER AT " + newPath)
+ helperClassVisitor.visitEnd()
+ super.visitEnd()
+ Files.createDirectories(newPath.getParent)
+ Files.write(newPath, helperClassVisitor.toByteArray)
+ }
+
+ private def mkHelperClass = {
+ val cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
+
+ cw.visit(targetByteCode,
+ ACC_PUBLIC + ACC_SUPER,
+ helperClassName,
+ null,
+ "java/lang/Object",
+ null)
+
+ cw.visitSource(s"$cName.java", null)
+
+ {
+ val mv = cw.visitMethod(ACC_PRIVATE, "", "()V", null, null)
+ mv.visitVarInsn(ALOAD, 0)
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false)
+ mv.visitInsn(RETURN)
+ mv.visitMaxs(0, 0)
+ mv.visitEnd()
+ }
+ cw
+ }
+
+ override def visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array[String]) = {
+ isInterface = (access & ACC_INTERFACE) != 0
+ cName = name
+ super.visit(targetByteCode, access, name, signature, superName, interfaces)
+ }
+}
+
+// works, strips the body
+class BodyStripper(newMethod: MethodVisitor) extends MethodVisitor(Opcodes.ASM5, newMethod) {
+ override def visitEnd() = {
+ newMethod.visitMaxs(0, 0)
+ super.visitEnd()
+ }
+}