mardi 6 octobre 2015

AbstractMethodError on call of a macro-generated method in a final class

I have a sealed trait BotUpdate and case classes which implement it. I created a macro annotation which adds a method for each BotUpdate implementation and a method def onUpdate(upd: BotUpdate): Unit which calls one of methods generated for updates, based on upd type.

package im.actor.bot.macros

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.language.postfixOps
import scala.reflect.macros.blackbox.Context

final class BotInterface extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro BotInterfaceImpl.impl
}

private object BotInterfaceImpl {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    val updates = c.mirror.staticClass("im.actor.bot.BotMessages.BotUpdate").knownDirectSubclasses collect {
      case cs: ClassSymbol ⇒ cs
    }

    val (updCallbacks, updDefs) = updates map { decl ⇒
      val methodName = s"on${decl.name.decodedName.toString}"
      val method = TermName(methodName)
      ((decl, method), q"def $method(upd: $decl): Unit")
    } unzip

    val updCases = updCallbacks map {
      case (upd, method) ⇒
        cq"""
           u: $upd => $method(u)
          """
    }

    val onUpdateDef =
      q"""def onUpdate(upd: im.actor.bot.BotMessages.BotUpdate): Unit = {
            upd match {
              case ..$updCases
            }
          }"""

    annottees map (_.tree) toList match {
      case q"$mods trait $traitName extends ..$parents { ..$body }" :: Nil ⇒
        c.Expr[Any](
          q"""
               $mods trait $traitName extends ..$parents {
                 ..$updDefs
                 $onUpdateDef
                 ..$body
               }
             """
        )
      case q"class $className(..$args) extends ..$parents { ..$body }" :: Nil ⇒
        c.Expr[Any](
          q"""
                class $className(..$args) extends ..$parents {
                  ..$updDefs
                  $onUpdateDef
                  ..$body
                }
            """
        )
      case unexpected ⇒ c.abort(c.enclosingPosition, "Invalid annottee")
    }
  }
}

and use it

package im.actor.bot

import im.actor.bot.macros.BotInterface

@BotInterface
trait BotBase

The problem is that when I call onUpdate method in a final class which implements BotBase, I get AbstractMethodError. It definitely implements all on* methods generated for updates.

java.lang.AbstractMethodError: im.actor.server.bot.InternalBot.onUpdate(Lim/actor/bot/BotMessages$BotUpdate;)

P.S. Another question is, you see this anti-DRY pattern matching in annottees map? Is it possible to make it DRY while using quasi-quotes for extracting type body and parents using?





Aucun commentaire:

Enregistrer un commentaire