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

com.github.plokhotnyuk.jsoniter_scala.macros.LowLevelQuoteUtil.scala Maven / Gradle / Ivy

package com.github.plokhotnyuk.jsoniter_scala.macros

import scala.quoted._

private[macros] object LowLevelQuoteUtil {

   /**
    * When we mix generation via trees and via quotes, we can have incorrect owners inside tree,
    * when tree with owner Symbol.currentOwner was inserted inside quote in other scope.
    *
    * We call deepChangeOwner when all val-s which defined in term is not used outside term
    * and over
    **/
  def deepChangeOwner(using Quotes)(tree: quotes.reflect.Tree, owner: quotes.reflect.Symbol, traceFlag: Boolean): quotes.reflect.Tree = {
    import quotes.reflect._

    val mapper = new TreeMap {
      override def transformTree(tree: Tree)(owner: Symbol): Tree =
        try super.transformTree(tree)(owner) catch {
          case ex: IllegalStateException =>
            if (traceFlag) println(s"${ex.getMessage()}\ntree=$tree")
            throw ex
        }

      override def transformStatement(tree: Statement)(owner: Symbol): Statement = tree match {
        case d: Definition =>
          if (d.symbol.owner != owner) throw new IllegalStateException("Invalid owner in definition.")
          d
        case _ => super.transformStatement(tree)(owner)
      }

      override def transformTerm(tree: Term)(owner: Symbol): Term = tree match {
        case Inlined(orgin, bindings, body) =>
          if (bindings.isEmpty) transformTerm(body)(owner)
          else super.transformTerm(tree)(owner)
        case bl@Block(statements, expr) =>
          var needTopLevelChange = false
          var incorrectSymbols: List[Symbol] = Nil
          statements.foreach {
            case d: Definition =>
              if (d.symbol.owner != owner) {
                incorrectSymbols = d.symbol :: incorrectSymbols
                needTopLevelChange = true
              }
            case other =>
          }
          val r =
            if (needTopLevelChange) {
               if (traceFlag) print(s"fixing owners for ${incorrectSymbols.mkString(",")}")
               bl.changeOwner(owner)
            } else bl
          val nStatements = r.statements.map {
            case t: Term => transformTerm(t)(owner)
            case other => other
          }
          val nExpr = transformTerm(r.expr)(owner)
          Block.copy(r)(nStatements, nExpr)
        case tif@If(cond, ifTrue, ifFalse) =>
          If.copy(tif)(transformTerm(cond)(owner), transformTerm(ifTrue)(owner), transformTerm(ifFalse)(owner))
        case _ =>
          super.transformTerm(tree)(owner)
      }

      override def transformTypeTree(tree: TypeTree)(owner: Symbol): TypeTree = tree // don't navigate over types.

      def checkInvalidOwner(term: Term, owner: Symbol): Boolean = checkOwner(term, owner, traceFlag, false, true)
    }
    mapper.transformTree(tree)(owner)
   }

  def checkOwner(using Quotes)(term: quotes.reflect.Term, ownerToCheck: quotes.reflect.Symbol, traceFlag: Boolean = true, throwFlag: Boolean = true, onlyFirst: Boolean = false): Boolean = {
    import quotes.reflect._

    var topLevelFound = false
    val treeTraverser = new TreeTraverser() {
      var wasException = false

      override def traverseTree(tree: Tree)(owner: Symbol): Unit = {
        if (topLevelFound && onlyFirst) return
        var foundInvalidOwner = false
        tree match {
          case d: Definition =>
            if (tree.symbol.owner != owner) {
              foundInvalidOwner = true
              topLevelFound = true
              if (traceFlag) println(s"checkOwner: owner mismatch for ${tree.show}, expectd owner: ${owner}, have ${tree.symbol.maybeOwner}")
              if (!wasException) {
                wasException = true
                throw new IllegalStateException(s"invlid owner, expected: ${owner}, have ${tree.symbol.owner}")
              }
            }
          case _ =>
        }
        try {
          if (!foundInvalidOwner) traverseTreeChildren(tree)(owner)
        } catch {
          case ex: IllegalStateException =>
            if (traceFlag) println(s"in tree:  $tree\n")
            if (throwFlag) throw ex
        }
     }
    }
    treeTraverser.foldTree((), term)(ownerToCheck)
    topLevelFound
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy