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

dotty.tools.dotc.transform.ProtectedAccessors.scala Maven / Gradle / Ivy

There is a newer version: 3.6.4-RC1-bin-20241220-0bfa1af-NIGHTLY
Show newest version
package dotty.tools.dotc
package transform

import core.Contexts.*
import core.NameKinds.*
import core.Symbols.*
import core.Flags.*
import core.Decorators.*
import core.Names.TermName
import MegaPhase.MiniPhase
import config.Printers.transforms
import dotty.tools.dotc.util.Property

/** Add accessors for all protected accesses. An accessor is needed if
 *  according to the rules of the JVM a protected class member is not accessible
 *  from the point of access, but is accessible if the access is from an enclosing
 *  class. In this point a public access method is placed in that enclosing class.
 */
object ProtectedAccessors {
  val name: String = "protectedAccessors"
  val description: String = "add accessors for protected members"

  /** Is the current context's owner inside the access boundary established by `sym`? */
  def insideBoundaryOf(sym: Symbol)(using Context): Boolean =
    if (sym.is(JavaDefined))
      sym.is(JavaStatic) ||  // Java's static protected definitions are treated as public
      ctx.owner.enclosingPackageClass == sym.enclosingPackageClass
    else {
      // For Scala-defined symbols we currently allow private and protected accesses
      // from inner packages, and compensate by widening accessibility of such symbols to public.
      // It would be good if we could revisit this at some point.
      val boundary = sym.accessBoundary(sym.enclosingPackageClass)
      ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass)
    }

  /** Do we need a protected accessor if the current context's owner
   *  is not in a subclass or subtrait of `sym`?
   */
  def needsAccessorIfNotInSubclass(sym: Symbol)(using Context): Boolean =
    sym.isTerm && sym.is(Protected) && !sym.hasPublicInBinary &&
    !sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public
    !insideBoundaryOf(sym)

  /** Do we need a protected accessor for accessing sym from the current context's owner? */
  def needsAccessor(sym: Symbol)(using Context): Boolean =
    needsAccessorIfNotInSubclass(sym) &&
    !needsAccessorIsSubclass(sym)

  def needsAccessorIsSubclass(sym: Symbol)(using Context): Boolean =
    ctx.owner.enclosingClass.derivesFrom(sym.owner)
}

class ProtectedAccessors extends MiniPhase {
  import ast.tpd.*

  override def phaseName: String = ProtectedAccessors.name

  override def description: String = ProtectedAccessors.description

  private val AccessorsKey = new Property.Key[Accessors]

  private def accessors(using Context): Accessors =
   ctx.property(AccessorsKey).get

  override def prepareForUnit(tree: Tree)(using Context): Context =
    ctx.fresh.setProperty(AccessorsKey, new Accessors)

  private class Accessors extends AccessProxies {
    val insert: Insert = new Insert {
      def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName = ProtectedAccessorName(name)
      def needsAccessor(sym: Symbol)(using Context) = ProtectedAccessors.needsAccessor(sym)

      override def ifNoHost(reference: RefTree)(using Context): Tree = {
        val curCls = ctx.owner.enclosingClass
        transforms.println(i"${curCls.ownersIterator.toList}%, %")
        report.error(em"illegal access to protected ${reference.symbol.showLocated} from $curCls",
          reference.srcPos)
        reference
      }
    }
  }

  override def transformIdent(tree: Ident)(using Context): Tree =
    accessors.insert.accessorIfNeeded(tree)

  override def transformSelect(tree: Select)(using Context): Tree =
    accessors.insert.accessorIfNeeded(tree)

  override def transformAssign(tree: Assign)(using Context): Tree =
    tree.lhs match {
      case lhs: RefTree if lhs.name.is(ProtectedAccessorName) =>
        cpy.Apply(tree)(accessors.insert.useSetter(lhs), tree.rhs :: Nil)
      case _ =>
        tree
    }

  override def transformTemplate(tree: Template)(using Context): Tree =
    cpy.Template(tree)(body = accessors.addAccessorDefs(tree.symbol.owner, tree.body))

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy