提交 cb3162d3 编写于 作者: 梦境迷离's avatar 梦境迷离

[WIP] simple di

上级 7a0fd152
/*
* Copyright (c) 2021 jxnu-liguobin && contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.dreamylost.di
import scala.reflect.macros.whitebox
import annotation.tailrec
/**
* DI Macro
*
* @author 梦境迷离
* @since 2021/7/22
* @version 1.0
*/
object DiMacro {
def AutoWire[T]: T = macro AutoWireMacro[T]
def AutoWireMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[T] = {
import c.universe._
// TODO: this should check if the found value uses AutoWire[]; now any value of the desired type will be returned
def findWiredOfType(t: Type): Option[Name] = {
@tailrec
def doFind(trees: List[Tree]): Option[Name] = trees match {
case Nil =>
c.error(c.enclosingPosition, s"Cannot find a wired value of type $t")
None
case tree :: tail => tree match {
// TODO: subtyping
case ValDef(_, name, tpt, _) if (tpt.tpe weak_<:< t) || (tpt.tpe <:< t) => Some(name.encodedName)
case DefDef(defDef) if (defDef._5.tpe weak_<:< t) || (defDef._5.tpe <:< t) => Some(defDef._2.decodedName)
case _ => doFind(tail)
}
}
val ClassDef(_, _, _, Template(_, _, body)) = c.enclosingClass
doFind(body)
}
val tType = implicitly[c.WeakTypeTag[T]]
val tConstructorOpt = tType.tpe.members.find(_.name.toTermName.toTermName.toString == "<init>")
val result = tConstructorOpt match {
case None =>
c.error(c.enclosingPosition, "Cannot find constructor for " + tType)
reify {
null.asInstanceOf[T]
}
case Some(tConstructor) =>
val params = tConstructor.asMethod.paramLists.flatten
val newT = Select(New(Ident(tType.tpe.typeSymbol)), termNames.CONSTRUCTOR)
val constructorParams = for (param <- params) yield {
val wireTo = findWiredOfType(param.typeSignature)
// If we cannot find a value of the given type, trying a by-name match, using the same name as the constructor's parameter.
.getOrElse(param.name)
Ident(wireTo)
}
val newTWithParams = Apply(newT, constructorParams)
c.info(c.enclosingPosition, s"Generated code: ${c.universe.show(newTWithParams)}", force = false)
c.Expr(newTWithParams)
}
result
}
}
/*
* Copyright (c) 2021 jxnu-liguobin && contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.dreamylost
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
/**
*
* @author li.guobin@immomo.com
* @version 1.0,2021/7/22
*/
class DiMacroTest extends AnyFlatSpec with Matchers {
"wire1" should "ok" in {
import io.github.dreamylost.di.DiMacro._
class A()
class B()
class C(a: A, b: B) {
override def toString = s"${super.toString}($a, $b)"
}
class D(b: B, c: C) {
override def toString = s"${super.toString}($b, $c)"
}
trait Service {
val a = AutoWire[A]
val bbb = AutoWire[B]
val c = AutoWire[C]
val d = AutoWire[D]
println(a)
println(bbb)
println(c)
println(d)
}
}
"wire2" should "ok by parameters inherit" in {
import io.github.dreamylost.di.DiMacro._
class A()
class B() extends A
class B1
class C(b1: B1, a: A) {
override def toString = s"${super.toString}($b1, $a)"
}
trait Service {
//The instance required by the constructor of the class injected by autowire must be visible within the scope of the enclosingClass.
val b1 = AutoWire[B1]
val b = AutoWire[B]
val c = AutoWire[C]
println(b1)
println(b)
println(c)
}
}
"wire3" should "ok by lazy and def" in {
import io.github.dreamylost.di.DiMacro._
class A()
class B() extends A
class B1
class C(b1: B1, a: A) {
override def toString = s"${super.toString}($b1, $a)"
}
trait Service {
lazy val b1 = AutoWire[B1]
def b: B = AutoWire[B]
val c = AutoWire[C]
println(b1)
println(b)
println(c)
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册