AbstractMacroProcessor.scala 12.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * 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.
 */

梦境迷离's avatar
梦境迷离 已提交
22
package io.github.dreamylost.macros
23 24

import scala.reflect.macros.whitebox
梦境迷离's avatar
梦境迷离 已提交
25

26 27 28
/**
 *
 * @author 梦境迷离
梦境迷离's avatar
梦境迷离 已提交
29
 * @since 2021/7/24
30 31
 * @version 1.0
 */
梦境迷离's avatar
梦境迷离 已提交
32
abstract class AbstractMacroProcessor(val c: whitebox.Context) {
梦境迷离's avatar
梦境迷离 已提交
33

梦境迷离's avatar
梦境迷离 已提交
34 35
  import c.universe._

梦境迷离's avatar
梦境迷离 已提交
36 37
  protected lazy val SDKClasses = Set("java.lang.Object", "scala.AnyRef")

梦境迷离's avatar
梦境迷离 已提交
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
  /**
   * Subclasses should override the method and return the final result abstract syntax tree, or an abstract syntax tree close to the final result.
   * When the macro implementation is very simple, we don't need to use this method, so we don't need to implement it.
   * When there are many macro input parameters, we will not use this method temporarily because we need to pass parameters.
   *
   * @param classDecl
   * @param compDeclOpt
   * @return c.Expr[Any], Why use Any? The dependent type need aux-pattern in scala2. Now let's get around this.
   *
   */
  def modifiedDeclaration(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = ???

  /**
   * Subclasses must override the method.
   *
   * @param annottees
   * @return Macro expanded final syntax tree.
   */
梦境迷离's avatar
梦境迷离 已提交
56
  def impl(annottees: Expr[Any]*): Expr[Any]
57

58 59 60 61 62 63 64
  /**
   * Eval tree.
   *
   * @param tree
   * @tparam T
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
65
  def evalTree[T: WeakTypeTag](tree: Tree): T = c.eval(c.Expr[T](c.untypecheck(tree.duplicate)))
66

梦境迷离's avatar
梦境迷离 已提交
67
  def extractArgumentsTuple1[T: WeakTypeTag](partialFunction: PartialFunction[Tree, Tuple1[T]]): Tuple1[T] = {
68 69 70
    partialFunction.apply(c.prefix.tree)
  }

梦境迷离's avatar
梦境迷离 已提交
71
  def extractArgumentsTuple2[T1: WeakTypeTag, T2: WeakTypeTag](partialFunction: PartialFunction[Tree, (T1, T2)]): (T1, T2) = {
72 73 74
    partialFunction.apply(c.prefix.tree)
  }

梦境迷离's avatar
梦境迷离 已提交
75
  def extractArgumentsTuple4[T1: WeakTypeTag, T2: WeakTypeTag, T3: WeakTypeTag, T4: WeakTypeTag](partialFunction: PartialFunction[Tree, (T1, T2, T3, T4)]): (T1, T2, T3, T4) = {
76 77 78
    partialFunction.apply(c.prefix.tree)
  }

梦境迷离's avatar
梦境迷离 已提交
79 80 81 82 83 84
  /**
   * Output ast result.
   *
   * @param force
   * @param resTree
   */
梦境迷离's avatar
梦境迷离 已提交
85
  def printTree(force: Boolean, resTree: Tree): Unit = {
86 87 88 89 90 91 92 93 94 95 96 97 98
    c.info(
      c.enclosingPosition,
      "\n###### Expanded macro ######\n" + resTree.toString() + "\n###### Expanded macro ######\n",
      force = force
    )
  }

  /**
   * Check the class and its companion object, and return the class definition.
   *
   * @param annottees
   * @return Return ClassDef
   */
梦境迷离's avatar
梦境迷离 已提交
99
  def checkAndGetClassDef(annottees: Expr[Any]*): ClassDef = {
梦境迷离's avatar
梦境迷离 已提交
100
    annottees.map(_.tree).toList match {
101 102
      case (classDecl: ClassDef) :: Nil => classDecl
      case (classDecl: ClassDef) :: (compDecl: ModuleDef) :: Nil => classDecl
梦境迷离's avatar
梦境迷离 已提交
103
      case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
104
    }
梦境迷离's avatar
梦境迷离 已提交
105 106
  }

107 108 109 110 111 112
  /**
   * Get companion object if it exists.
   *
   * @param annottees
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
113
  def tryGetCompanionObject(annottees: Expr[Any]*): Option[ModuleDef] = {
114 115 116
    annottees.map(_.tree).toList match {
      case (classDecl: ClassDef) :: Nil => None
      case (classDecl: ClassDef) :: (compDecl: ModuleDef) :: Nil => Some(compDecl)
梦境迷离's avatar
梦境迷离 已提交
117
      case (compDecl: ModuleDef) :: Nil => Some(compDecl)
梦境迷离's avatar
梦境迷离 已提交
118
      case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
梦境迷离's avatar
梦境迷离 已提交
119 120 121 122 123
    }
  }

  /**
   * Wrap tree result with companion object.
梦境迷离's avatar
梦境迷离 已提交
124
   *
梦境迷离's avatar
梦境迷离 已提交
125 126 127 128
   * @param resTree class
   * @param annottees
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
129 130
  def treeResultWithCompanionObject(resTree: Tree, annottees: Expr[Any]*): Tree = {
    val companionOpt = tryGetCompanionObject(annottees: _*)
梦境迷离's avatar
梦境迷离 已提交
131
    companionOpt.fold(resTree) { t =>
梦境迷离's avatar
梦境迷离 已提交
132 133
      q"""
         $resTree
梦境迷离's avatar
梦境迷离 已提交
134
         $t
梦境迷离's avatar
梦境迷离 已提交
135
         """
136 137 138 139 140 141 142
    }
  }

  /**
   * Modify the associated object itself according to whether there is an associated object.
   *
   * @param annottees
梦境迷离's avatar
梦境迷离 已提交
143
   * @param modifyAction The actual processing function
144 145
   * @return Return the result of modifyAction
   */
梦境迷离's avatar
梦境迷离 已提交
146 147
  def handleWithImplType(annottees: Expr[Any]*)
    (modifyAction: (ClassDef, Option[ModuleDef]) => Any): Expr[Nothing] = {
148
    annottees.map(_.tree) match {
梦境迷离's avatar
梦境迷离 已提交
149 150
      case (classDecl: ClassDef) :: Nil => modifyAction(classDecl, None).asInstanceOf[Expr[Nothing]]
      case (classDecl: ClassDef) :: (compDecl: ModuleDef) :: Nil => modifyAction(classDecl, Some(compDecl)).asInstanceOf[Expr[Nothing]]
梦境迷离's avatar
梦境迷离 已提交
151
      case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
152 153 154 155 156 157 158 159 160
    }
  }

  /**
   * Expand the class and check whether the class is a case class.
   *
   * @param annotateeClass classDef
   * @return Return true if it is a case class
   */
梦境迷离's avatar
梦境迷离 已提交
161
  def isCaseClass(annotateeClass: ClassDef): Boolean = {
162 163
    annotateeClass match {
      case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
梦境迷离's avatar
梦境迷离 已提交
164
        mods.asInstanceOf[Modifiers].hasFlag(Flag.CASE)
梦境迷离's avatar
梦境迷离 已提交
165
      case _ => c.abort(c.enclosingPosition, ErrorMessage.ONLY_CLASS)
166 167 168 169
    }
  }

  /**
梦境迷离's avatar
梦境迷离 已提交
170
   * Expand the constructor and get the field TermName.
171 172 173 174
   *
   * @param field
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
175
  def getFieldTermName(field: Tree): TermName = {
176
    field match {
梦境迷离's avatar
梦境迷离 已提交
177 178
      case q"$mods val $tname: $tpt = $expr" => tname.asInstanceOf[TermName]
      case q"$mods var $tname: $tpt = $expr" => tname.asInstanceOf[TermName]
梦境迷离's avatar
梦境迷离 已提交
179 180 181 182 183 184 185 186 187 188 189
      case q"$mods val $pat = $expr"         => pat.asInstanceOf[TermName] //for equalsAndHashcode, need contains all fields.
      case q"$mods var $pat = $expr"         => pat.asInstanceOf[TermName]
    }
  }

  /**
   * Expand the method params and get the param Name.
   *
   * @param field
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
190
  def getMethodParamName(field: Tree): Name = {
梦境迷离's avatar
梦境迷离 已提交
191 192
    val q"$mods val $tname: $tpt = $expr" = field
    tpt.asInstanceOf[Ident].name.decodedName
梦境迷离's avatar
梦境迷离 已提交
193 194 195 196 197 198 199 200
  }

  /**
   * Check whether the mods of the fields has a `private[this]`, because it cannot be used in equals method.
   *
   * @param field
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
201
  def classParamsIsNotPrivate(field: Tree): Boolean = {
梦境迷离's avatar
梦境迷离 已提交
202 203 204
    field match {
      case q"$mods val $tname: $tpt = $expr" => if (mods.asInstanceOf[Modifiers].hasFlag(Flag.PRIVATE)) false else true
      case q"$mods var $tname: $tpt = $expr" => true
205 206 207
    }
  }

梦境迷离's avatar
梦境迷离 已提交
208
  /**
梦境迷离's avatar
梦境迷离 已提交
209 210
   * Expand the constructor and get the field with assign.
   *
梦境迷离's avatar
梦境迷离 已提交
211 212 213
   * @param annotteeClassParams
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
214
  def getFieldAssignExprs(annotteeClassParams: Seq[Tree]): Seq[Tree] = {
梦境迷离's avatar
梦境迷离 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227 228
    annotteeClassParams.map {
      case q"$mods var $tname: $tpt = $expr" => q"$tname: $tpt" //Ignore expr
      case q"$mods val $tname: $tpt = $expr" => q"$tname: $tpt"
    }
  }

  /**
   * Modify companion objects.
   *
   * @param compDeclOpt
   * @param codeBlock
   * @param className
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
229 230 231
  def modifiedCompanion(
    compDeclOpt: Option[ModuleDef],
    codeBlock:   Tree, className: TypeName): Tree = {
232 233 234 235 236 237
    compDeclOpt map { compDecl =>
      val q"$mods object $obj extends ..$bases { ..$body }" = compDecl
      val o =
        q"""
          $mods object $obj extends ..$bases {
            ..$body
梦境迷离's avatar
梦境迷离 已提交
238
            ..$codeBlock
239 240 241 242 243 244
          }
        """
      c.info(c.enclosingPosition, s"modifiedCompanion className: $className, exists obj: $o", force = true)
      o
    } getOrElse {
      // Create a companion object with the builder
梦境迷离's avatar
梦境迷离 已提交
245
      val o = q"object ${className.toTermName} { ..$codeBlock }"
246 247 248 249
      c.info(c.enclosingPosition, s"modifiedCompanion className: $className, new obj: $o", force = true)
      o
    }
  }
梦境迷离's avatar
梦境迷离 已提交
250 251 252 253

  /**
   * Extract the internal fields of members belonging to the class, but not in primary constructor.
   *
梦境迷离's avatar
梦境迷离 已提交
254
   * @param annotteeClassDefinitions
梦境迷离's avatar
梦境迷离 已提交
255
   */
梦境迷离's avatar
梦境迷离 已提交
256
  def getClassMemberValDefs(annotteeClassDefinitions: Seq[Tree]): Seq[Tree] = {
梦境迷离's avatar
梦境迷离 已提交
257 258
    annotteeClassDefinitions.filter(p => p match {
      case _: ValDef => true
梦境迷离's avatar
梦境迷离 已提交
259 260 261 262 263 264 265 266 267
      case _         => false
    })
  }

  /**
   * Extract the methods belonging to the class, contains Secondary Constructor.
   *
   * @param annotteeClassDefinitions
   */
梦境迷离's avatar
梦境迷离 已提交
268
  def getClassMemberDefDefs(annotteeClassDefinitions: Seq[Tree]): Seq[Tree] = {
梦境迷离's avatar
梦境迷离 已提交
269 270 271
    annotteeClassDefinitions.filter(p => p match {
      case _: DefDef => true
      case _         => false
梦境迷离's avatar
梦境迷离 已提交
272 273
    })
  }
梦境迷离's avatar
梦境迷离 已提交
274 275 276 277 278 279 280 281

  /**
   * We generate constructor with currying, and we have to deal with the first layer of currying alone.
   *
   * @param typeName
   * @param fieldss
   * @param isCase
   * @return A constructor with currying, it not contains tpt, provide for calling method.
梦境迷离's avatar
梦境迷离 已提交
282
   * @example {{ new TestClass12(i)(j)(k)(t) }}
梦境迷离's avatar
梦境迷离 已提交
283
   */
梦境迷离's avatar
梦境迷离 已提交
284 285
  def getConstructorWithCurrying(typeName: TypeName, fieldss: List[List[Tree]], isCase: Boolean): Tree = {
    val allFieldsTermName = fieldss.map(f => f.map(ff => getFieldTermName(ff)))
梦境迷离's avatar
梦境迷离 已提交
286 287 288 289 290 291 292
    // not currying
    val constructor = if (fieldss.isEmpty || fieldss.size == 1) {
      q"${if (isCase) q"${typeName.toTermName}(..${allFieldsTermName.flatten})" else q"new $typeName(..${allFieldsTermName.flatten})"}"
    } else {
      // currying
      val first = allFieldsTermName.head
      if (isCase) q"${typeName.toTermName}(...$first)(...${allFieldsTermName.tail})"
梦境迷离's avatar
梦境迷离 已提交
293
      else q"new $typeName(..$first)(...${allFieldsTermName.tail})"
梦境迷离's avatar
梦境迷离 已提交
294 295 296 297 298 299 300 301 302 303 304
    }
    c.info(c.enclosingPosition, s"getConstructorWithCurrying constructor: $constructor, paramss: $fieldss", force = true)
    constructor
  }

  /**
   * We generate apply method with currying, and we have to deal with the first layer of currying alone.
   *
   * @param typeName
   * @param fieldss
   * @return A apply method with currying.
梦境迷离's avatar
梦境迷离 已提交
305
   * @example {{ def apply(int: Int)(j: Int)(k: Option[String])(t: Option[Long]): B3 = new B3(int)(j)(k)(t) }}
梦境迷离's avatar
梦境迷离 已提交
306
   */
梦境迷离's avatar
梦境迷离 已提交
307 308 309
  def getApplyMethodWithCurrying(typeName: TypeName, fieldss: List[List[Tree]], classTypeParams: List[Tree]): Tree = {
    val allFieldsTermName = fieldss.map(f => getFieldAssignExprs(f))
    val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
梦境迷离's avatar
梦境迷离 已提交
310 311
    // not currying
    val applyMethod = if (fieldss.isEmpty || fieldss.size == 1) {
梦境迷离's avatar
梦境迷离 已提交
312
      q"def apply[..$classTypeParams](..${allFieldsTermName.flatten}): $typeName[..$returnTypeParams] = ${getConstructorWithCurrying(typeName, fieldss, isCase = false)}"
梦境迷离's avatar
梦境迷离 已提交
313 314 315
    } else {
      // currying
      val first = allFieldsTermName.head
梦境迷离's avatar
梦境迷离 已提交
316
      q"def apply[..$classTypeParams](..$first)(...${allFieldsTermName.tail}): $typeName[..$returnTypeParams] = ${getConstructorWithCurrying(typeName, fieldss, isCase = false)}"
梦境迷离's avatar
梦境迷离 已提交
317
    }
梦境迷离's avatar
梦境迷离 已提交
318
    c.info(c.enclosingPosition, s"getApplyMethodWithCurrying constructor: $applyMethod, paramss: $fieldss", force = true)
梦境迷离's avatar
梦境迷离 已提交
319 320
    applyMethod
  }
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349

  /**
   * Only for primitive types, we can get type and map to scala type.
   *
   * @param jType java type name
   * @return Scala type name
   */
  def toScalaType(jType: String): String = {
    val types = Map(
      "java.lang.Integer" -> "Int",
      "java.lang.Long" -> "Long",
      "java.lang.Double" -> "Double",
      "java.lang.Float" -> "Float",
      "java.lang.Short" -> "Short",
      "java.lang.Byte" -> "Byte",
      "java.lang.Boolean" -> "Boolean",
      "java.lang.Character" -> "Char",
      "java.lang.String" -> "String"
    )
    types.getOrElse(jType, jType)
  }

  /**
   * Gets a list of generic parameters.
   * This is because the generic parameters of a class cannot be used directly in the return type, and need to be converted.
   *
   * @param tpParams
   * @return
   */
梦境迷离's avatar
梦境迷离 已提交
350
  def extractClassTypeParamsTypeName(tpParams: List[Tree]): List[TypeName] = {
梦境迷离's avatar
梦境迷离 已提交
351
    tpParams.map(_.asInstanceOf[TypeDef].name)
352
  }
梦境迷离's avatar
梦境迷离 已提交
353

梦境迷离's avatar
梦境迷离 已提交
354 355
  /**
   * Is there a parent class? Does not contains sdk class, such as AnyRef Object
梦境迷离's avatar
梦境迷离 已提交
356
   *
梦境迷离's avatar
梦境迷离 已提交
357 358 359 360 361 362 363
   * @param superClasses
   * @return
   */
  def existsSuperClassExcludeSdkClass(superClasses: Seq[Tree]): Boolean = {
    superClasses.nonEmpty && !superClasses.forall(sc => SDKClasses.contains(sc.toString()))
  }

364
}