Skip to content
Merged
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 @@ -11,7 +11,6 @@ import wvlet.log.LogSupport
private[airframe] trait DesignImpl extends LogSupport:
self: Design =>
inline def bind[A]: Binder[A] =
registerTraitFactory[A]
new Binder(self, Surface.of[A], SourceCode()).asInstanceOf[Binder[A]]

inline def remove[A]: Design =
Expand Down
105 changes: 0 additions & 105 deletions airframe-di/src/main/scala-3/wvlet/airframe/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,108 +41,3 @@ val traitFactoryCache = new ConcurrentHashMap[Surface, Session => Any].asScala

def getOrElseUpdateTraitFactoryCache(s: Surface, factory: Session => Any): Session => Any =
traitFactoryCache.getOrElseUpdate(s, factory)

@deprecated("Instantiating trait with DI is still experimental in Scala 3", "23.9.1")
inline def registerTraitFactory[A]: Unit = {
// registerTraitFactoryImpl[A]
}

import scala.quoted.*

private def shouldGenerateTrait[A](using
tpe: Type[A],
q: Quotes
): Boolean =
import quotes.*
import quotes.reflect.*

val t = TypeRepr.of[A]
val a = t.typeSymbol

// Find the public default constructor that has no arguments
val hasPublicDefaultConstructor: Boolean =
val pc = a.primaryConstructor
pc.paramSymss.size == 1 && pc.paramSymss(0).size == 0

val hasAbstractMethods: Boolean =
a.methodMembers.exists { x =>
x.flags.is(Flags.Method) &&
(x.flags.is(Flags.Abstract) || x.flags.is(Flags.Deferred))
}
val isTaggedType = a.fullName.startsWith("wvlet.airframe.surface.tag.")
val isSealedType = a.flags.is(Flags.Sealed)
val isStatic = a.flags.is(Flags.JavaStatic)
val isLocal = a.flags.is(Flags.Local)
val isTrait = a.flags.is(Flags.Trait)

val shouldInstantiateTrait =
if !isStatic then
// = Non static type
// If X is non static type (= local class or trait),
// we need to instantiate it first in order to populate its $outer variables

// We cannot instantiate path-dependent types
if a.fullName.contains("#") then false
else !hasAbstractMethods && hasPublicDefaultConstructor
else if a.isAbstractType then
// = Abstract type
// We cannot build abstract type X that has abstract methods, so bind[X].to[ConcreteType]
// needs to be found in the design

// If there is no abstract methods, it might be a trait without any method
!hasAbstractMethods
else
// We cannot instantiate any trait or class without the default constructor
// So binding needs to be found in the Design.
hasPublicDefaultConstructor

// Tagged type or sealed class binding should be found in Design
val result = isTrait && !isTaggedType && !isSealedType && shouldInstantiateTrait

// println(s"${a.flags.show}, isStatic: ${isStatic}, isAbstract: ${a.isAbstractType}, isSealed: ${isSealedType} ${a.fullName}, " +
// s"has pstr: ${hasPublicDefaultConstructor} is tagged: ${isTaggedType}, has abstract method: ${hasAbstractMethods}")

result

@experimental def registerTraitFactoryImpl[A](using
tpe: Type[A],
q: Quotes
): quoted.Expr[Unit] =
import quotes.*
import quotes.reflect.*

if !shouldGenerateTrait[A] then '{}
else
val name = "$anon"
val parents = List(TypeTree.of[Object], TypeTree.of[A], TypeTree.of[DISupport])

def decls(cls: Symbol): List[Symbol] =
List(Symbol.newMethod(cls, "session", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Session])))

val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
val sessionMethodSym = cls.declaredMethod("session").head
def sessionMethodDef(s: Term) = DefDef(sessionMethodSym, argss => Some(s))
def newCls(s: Term) =
val clsDef = ClassDef(cls, parents, body = List(sessionMethodDef(s)))
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[A])
Block(List(clsDef), newCls)

// Generate code { (s: Session) => new A with DISupport { def sesion: Session = s } }
val body = Lambda(
owner = Symbol.spliceOwner,
tpe = MethodType(List("s"))(_ => List(TypeRepr.of[Session]), _ => TypeRepr.of[A]),
rhsFn = (sym: Symbol, paramRefs: List[Tree]) =>
val s = paramRefs.head.asExprOf[Session].asTerm
val fn = newCls(s)
fn.changeOwner(sym)
)
// Register trait factory
// { (s: Session) => new A with DISupport { def session = s } }
'{
wvlet.airframe.getOrElseUpdateTraitFactoryCache(
Surface.of[A],
${
body.asExprOf[Session => Any]
}
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,9 @@ private[router] object RouterObjectMacros:

if TypeRepr.of[Controller] <:< TypeRepr.of[HttpFilterType] then
'{
wvlet.airframe.registerTraitFactory[Controller]
Router(filterSurface = Some(Surface.of[Controller]))
}
else
'{
wvlet.airframe.registerTraitFactory[Controller]
Router.empty.add[Controller]
}
Loading