All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.thoughtworks.Override.scala Maven / Gradle / Ivy

The newest version!
package com.thoughtworks
import com.thoughtworks.DelayMacros.DelayTreeCreator
import macrocompat.bundle
import shapeless._

import scala.annotation.StaticAnnotation
import scala.collection.immutable.Queue
import scala.language.dynamics
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
import scala.util.control.NonFatal

/**
  * @author 杨博 (Yang Bo) <[email protected]>
  */
final class Override[Vals, Result](val newInstanceRecord: Vals => Result) extends AnyVal with Dynamic {
  def applyDynamic(method: String)(): Any = macro RecordMacros.forwardImpl

  /**
    * @usecase def newInstance(vals: Any*): Result = ???
    */
  def applyDynamicNamed(method: String)(rec: Any*): Any = macro RecordMacros.forwardNamedImpl
}

object Override {

  final class inject extends StaticAnnotation

  private[Override] final class PartiallyAppliedNewInstance[Result] extends Dynamic {
    def applyRecord[Vals](vals: Vals)(implicit cachedOverride: Override[Vals, Result]): Result = {
      cachedOverride.newInstanceRecord(vals)
    }

    def applyDynamic[Issues10307Workaround](method: String)(): Any = macro RecordMacros.forwardImpl

    def applyDynamicNamed[Issues10307Workaround](method: String)(rec: Any*): Any = macro RecordMacros.forwardNamedImpl
  }

  /** @usecase def newInstance[Result](vals: Any*): Result = ???
    */
  def newInstance[Result]: PartiallyAppliedNewInstance[Result] = new PartiallyAppliedNewInstance[Result]

  def apply[Vals, Result](implicit `override`: Override[Vals, Result]): Override[Vals, Result] = `override`

  implicit def materialize[Vals, Result]: Override[Vals, Result] = macro Macros.materialize[Vals, Result]

  @bundle
  private[Override] final class Macros(val c: whitebox.Context)
      extends CaseClassMacros
      with SingletonTypeUtils
      with DelayMacros {
    import c.universe._

    private val injectType = typeOf[inject]
    private def demixin(t: Type): Stream[Type] = {
      t.dealias match {
        case RefinedType(superTypes, refinedScope) if refinedScope.isEmpty =>
          superTypes.toStream.flatMap(demixin)
        case notRefinedType =>
          Stream(notRefinedType)
      }
    }

    def materialize[Vals: WeakTypeTag, Result: WeakTypeTag]: Tree =
      try {
        val mixinType = weakTypeOf[Result]
        val valsType = weakTypeOf[Vals]
        val valTypes = unpackHListTpe(valsType)
        object DealiasFieldType {
          def unapply(arg: Type): Option[(String, Type)] = arg.dealias match {
            case FieldType(keyType, v) =>
              keyType.dealias match {
                case SingletonSymbolType(k) =>
                  Some(k, v)
                case _ => None
              }
            case _ => None
          }
        }
        val (temporaryNames, upvalues) = (for (DealiasFieldType(k, v) <- valTypes) yield {
          val overridingName = TermName(k)
          val setters = for {
            alternative <- mixinType.member(overridingName).alternatives
            setter = alternative.asMethod.setter
            if setter != NoSymbol
          } yield setter
          val temporaryName = TermName(c.freshName())
          val upvalue = if (setters.isEmpty || setters.exists(!_.isAbstract)) {
            q"override val $overridingName: $v = $temporaryName"
          } else {
            q"override var $overridingName: $v = $temporaryName"
          }
          (temporaryName, upvalue)
        }).unzip
        val pattern = valTypes.view.zip(temporaryNames).foldRight[Tree](pq"_root_.shapeless.HNil") {
          case ((DealiasFieldType(k, v), temporaryName), accumulator) =>
            pq"_root_.shapeless.::($temporaryName, $accumulator)"
        }
        val valuesType = mkHListTpe(for (DealiasFieldType(_, v) <- valTypes) yield v)
        val argumentHListName = TermName(c.freshName("argumentHList"))

        val injects = for {
          baseClass <- mixinType.baseClasses.reverse
          member <- baseClass.info.decls
          if member.isMethod && {
            c.internal.initialize(member)
            member.annotations.exists { a =>
              a.tree.tpe <:< injectType
            }
          }
        } yield {
          delayValOrDef(new DelayTreeCreator {
            override def apply(c: whitebox.Context): c.universe.Tree =
              try {
                import c.universe._

                val memberSymbol = member.asInstanceOf[Symbol].asMethod
                val methodName = memberSymbol.name.toTermName

//                def  narrowSuperType = internal.superType(internal.thisType(c.internal.enclosingOwner),
//                                                         internal.thisType(memberSymbol.owner))
//
//                val methodType = memberSymbol.infoIn(narrowSuperType) // TODO: need Type To Tree conversion for better this.type
                val methodType = memberSymbol.info
                val result = if (memberSymbol.isVar) {
                  q"override var $methodName = _root_.scala.Predef.implicitly"
                } else if (memberSymbol.isVal) {
                  q"override val $methodName = _root_.scala.Predef.implicitly"
                } else {
                  val argumentTrees = methodType.paramLists.map(_.map { argumentSymbol =>
                    if (argumentSymbol.asTerm.isImplicit) {
                      q"implicit val ${argumentSymbol.name.toTermName}: ${argumentSymbol.info}"
                    } else {
                      q"val ${argumentSymbol.name.toTermName}: ${argumentSymbol.info}"
                    }
                  })
                  q"override def $methodName[..${methodType.typeArgs}](...$argumentTrees) = _root_.scala.Predef.implicitly"
                }
//                c.info(c.enclosingPosition, show(result), true)
                result
              } catch {
                case NonFatal(e) =>
                  e.printStackTrace()
                  throw e
              }
          })
        }
        val overridenTypes =
          (for {
            baseClass <- mixinType.baseClasses.reverse
            member <- baseClass.info.decls
            if member.isType
          } yield member)
            .groupBy(_.name.toString)
            .withFilter {
              _._2.forall {
                _.info match {
                  case TypeBounds(_, _) => true
                  case _ => false
                }
              }
            }
            .map {
              case (name, members) =>
                delayType(new DelayTreeCreator {
                  override def apply(c: whitebox.Context): c.universe.Tree =
                    try {
                      import c.universe._

                      val lowerBounds = members.collect(scala.Function.unlift { member =>
                        val memberSymbol = member.asInstanceOf[Symbol]
                        val narrowSuperType = internal.superType(internal.thisType(c.internal.enclosingOwner),
                                                                 internal.thisType(memberSymbol.owner))
                        val TypeBounds(_, lowerBound) = memberSymbol.infoIn(narrowSuperType)
                        if (lowerBound =:= definitions.AnyTpe) {
                          None
                        } else {
                          Some(tq"$lowerBound")
                        }
                      })
                      val compoundTypeTree = CompoundTypeTree(Template(lowerBounds.toList, noSelfType, Nil))
                      val result = q"override type ${TypeName(name)} = $compoundTypeTree"
//                      c.info(c.enclosingPosition, show(result), true)
                      result
                    } catch {
                      case NonFatal(e) =>
                        e.printStackTrace()
                        throw e
                    }
                })
            }
        val superTypes: Stream[c.universe.Type] = demixin(mixinType)

        val superTrees = for (superType <- superTypes) yield {
          tq"$superType"
        }
        val result = q"""
        new _root_.com.thoughtworks.Override[$valsType, $mixinType]((_: $valuesType) match { case $pattern =>
          new ..$superTrees {
            ..$upvalues
            ..$overridenTypes
            ..$injects
          }
        })
      """
//        c.info(c.enclosingPosition, showCode(result), false)
        result
      } catch {
        case NonFatal(e) =>
          e.printStackTrace()
          throw e
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy