Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autowire factory methods extended support scala2 #186

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,97 @@ object MacwireMacros {
}
}

def autowire_impl[T: c.WeakTypeTag](
c: blackbox.Context
)(dependencies: c.Expr[Any]*): c.Expr[T] = {
import c.universe._

val targetType = implicitly[c.WeakTypeTag[T]]
lazy val typeCheckUtil = new TypeCheckUtil[c.type](c, log)

sealed trait Provider {
def `type`: Type
}

case class Instance(value: Tree) extends Provider {
lazy val `type`: Type = typeCheckUtil.typeCheckIfNeeded(value)
lazy val ident: Tree = value
}

class FactoryMethod(params: List[ValDef], fun: Tree) extends Provider {

def result(resolver: Type => Tree): Instance = new Instance(applyWith(resolver))

lazy val `type`: Type = fun.symbol.asMethod.returnType

def applyWith(resolver: Type => Tree): Tree = {
val values = params.map { case ValDef(_, name, tpt, rhs) =>
resolver(typeCheckUtil.typeCheckIfNeeded(tpt))
}

q"$fun(..$values)"
}

}

object FactoryMethod {
def fromTree(tree: Tree): Option[FactoryMethod] = tree match {
// Function with two parameter lists (implicit parameters) (<2.13)
case Block(Nil, Function(p, Apply(Apply(f, _), _))) => Some(new FactoryMethod(p, f))
case Block(Nil, Function(p, Apply(f, _))) => Some(new FactoryMethod(p, f))
// Function with two parameter lists (implicit parameters) (>=2.13)
case Function(p, Apply(Apply(f, _), _)) => Some(new FactoryMethod(p, f))
case Function(p, Apply(f, _)) => Some(new FactoryMethod(p, f))
// Other types not supported
case _ => None
}

}

def providerFromExpr(expr: Expr[Any]): Provider = {
val tree = expr.tree
FactoryMethod.fromTree(tree)
.getOrElse(new Instance(tree))
}

val providers = dependencies.map(providerFromExpr)

log(s"PURE exprs: s[${dependencies.mkString(", ")}]")
log(s"PURE Providers: [${providers.mkString(", ")}]")
log(s"PURE ProvidersTYPES: [${providers.map(_.`type`).mkString(", ")}]")

def findProvider(tpe: Type): Option[Tree] = providers.find(_.`type` <:< tpe).map {
case i: Instance => i.ident
case fm: FactoryMethod => fm.result(findProvider(_).getOrElse(c.abort(c.enclosingPosition, "TODO3"))).ident
}

val code = wireWithResolver(c)(findProvider(_)) getOrElse c.abort(c.enclosingPosition, s"Failed for [$targetType]")//FIXME Improve error tracing
log(s"Code: [$code]")

c.Expr[T](code)
}

def wireWithResolver[T: c.WeakTypeTag](
c: blackbox.Context
)(resolver: c.Type => Option[c.Tree]) = {
import c.universe._

def isWireable(tpe: Type): Boolean = {
val name = tpe.typeSymbol.fullName

!name.startsWith("java.lang.") && !name.startsWith("scala.")
}

lazy val resolutionWithFallback: (Symbol, Type) => Tree = (_, tpe) =>
if (isWireable(tpe)) resolver(tpe).orElse(go(tpe)).getOrElse(c.abort(c.enclosingPosition, s"TODO???"))
else c.abort(c.enclosingPosition, s"Cannot find a value of type: [${tpe}]")

def go(t: Type): Option[Tree] =
(ConstructorCrimper.constructorTree(c, log)(t, resolutionWithFallback) orElse CompanionCrimper
.applyTree(c, log)(t, resolutionWithFallback))


go(implicitly[c.WeakTypeTag[T]].tpe)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,32 @@ object CompanionCrimper {
} yield pl.foldLeft(applyMethod)((acc: Tree, args: List[Tree]) => Apply(acc, args))
}

def applyTreeV2[C <: blackbox.Context](
c: C,
log: Logger
)(targetType: c.Type, resolver: (c.Symbol, c.Type) => Option[c.Tree]): Option[c.Tree] = {
import c.universe._

lazy val apply: Option[Symbol] = CompanionCrimper
.applies(c, log)(targetType)
.flatMap(_ match {
case applyMethod :: Nil => Some(applyMethod)
case _ => None
})

lazy val applySelect: Option[Select] = apply.map(a => Select(Ident(targetType.typeSymbol.companion), a))

lazy val applyParamLists: Option[List[List[Symbol]]] = apply.map(_.asMethod.paramLists)

def wireParams(paramLists: List[List[Symbol]]): List[List[Option[Tree]]] =
paramLists.map(_.map(p => resolver(p, p.typeSignature)))

def applyArgs: Option[List[List[Option[Tree]]]] = applyParamLists.map(x => wireParams(x))

for {
pl: List[List[Tree]] <- applyArgs.flatMap(x => sequence(x.map(sequence)))
applyMethod: Tree <- applySelect
} yield pl.foldLeft(applyMethod)((acc: Tree, args: List[Tree]) => Apply(acc, args))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,31 @@ object ConstructorCrimper {
constructorArgs.map(_.foldLeft(constructionMethodTree)((acc: Tree, args: List[Tree]) => Apply(acc, args)))
}
}

def constructorTreeV2[C <: blackbox.Context](
c: C,
log: Logger
)(targetType: c.Type, resolver: (c.Symbol, c.Type) => Option[c.Tree]): Option[c.Tree] = {
import c.universe._

lazy val targetTypeD: Type = targetType.dealias

lazy val constructor: Option[Symbol] = ConstructorCrimper.constructor(c, log)(targetType)

lazy val constructorParamLists: Option[List[List[Symbol]]] =
constructor.map(_.asMethod.paramLists.filterNot(_.headOption.exists(_.isImplicit)))

def constructorArgs: Option[List[List[Option[Tree]]]] = log.withBlock("Looking for targetConstructor arguments") {
constructorParamLists.map(wireConstructorParams(_))
}

def wireConstructorParams(paramLists: List[List[Symbol]]): List[List[Option[Tree]]] =
paramLists.map(_.map(p => resolver(p, /*SI-4751*/ paramType(c)(targetTypeD, p))))

log.withBlock(s"Creating Constructor Tree for $targetType") {
val constructionMethodTree: Tree = Select(New(Ident(targetTypeD.typeSymbol)), termNames.CONSTRUCTOR)
val flattenConstructorArgs = constructorArgs.flatMap(l => sequence(l.map(sequence)))//FIXME it breaks the current macwire's philosophy....
flattenConstructorArgs.map(_.foldLeft(constructionMethodTree)((acc: Tree, args: List[Tree]) => Apply(acc, args)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ package object macwire {

def wireRec[T]: T = macro MacwireMacros.wireRec_impl[T]

def autowire[T](dependencies: Any*): T = macro MacwireMacros.autowire_impl[T]

}
Loading