|
| 1 | +package dotty.tools.dotc |
| 2 | +package transform |
| 3 | + |
| 4 | +import core.Contexts.Context |
| 5 | +import core.NameKinds._ |
| 6 | +import core.Symbols._ |
| 7 | +import core.Flags._ |
| 8 | +import core.Decorators._ |
| 9 | +import MegaPhase.MiniPhase |
| 10 | +import ast.Trees._ |
| 11 | +import util.Property |
| 12 | + |
| 13 | +/** Add accessors for all protected accesses. An accessor is needed if |
| 14 | + * according to the rules of the JVM a protected class member is not accesissible |
| 15 | + * from the point of access, but is accessible if the access is from an enclosing |
| 16 | + * class. In this point a public access method is placed in that enclosing class. |
| 17 | + */ |
| 18 | +object ProtectedAccessors { |
| 19 | + val name = "protectedAccessors" |
| 20 | + |
| 21 | + private val LHS = new Property.StickyKey[Unit] |
| 22 | + |
| 23 | + /** Is the current context's owner inside the access boundary established by `sym`? */ |
| 24 | + def insideBoundaryOf(sym: Symbol)(implicit ctx: Context): Boolean = { |
| 25 | + if (sym.is(JavaDefined)) { |
| 26 | + sym.is(JavaStatic) || // Java's static protected definitions are treated as public |
| 27 | + ctx.owner.enclosingPackageClass == sym.enclosingPackageClass |
| 28 | + } |
| 29 | + else { |
| 30 | + // For Scala-defined symbols we currently allow private and protected accesses |
| 31 | + // from inner packages, and compensate by widening accessibility of such symbols to public. |
| 32 | + // It would be good if we could revisit this at some point. |
| 33 | + val boundary = sym.accessBoundary(sym.enclosingPackageClass) |
| 34 | + ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass) |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + /** Do we need a protected accessor if the current context's owner |
| 39 | + * is not in a subclass or subtrait of `sym`? |
| 40 | + */ |
| 41 | + def needsAccessorIfNotInSubclass(sym: Symbol)(implicit ctx: Context): Boolean = |
| 42 | + sym.isTerm && sym.is(Protected) && |
| 43 | + !sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public |
| 44 | + !insideBoundaryOf(sym) |
| 45 | + |
| 46 | + /** Do we need a protected accessor for accessing sym from the current context's owner? */ |
| 47 | + def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean = |
| 48 | + needsAccessorIfNotInSubclass(sym) && |
| 49 | + !ctx.owner.enclosingClass.derivesFrom(sym.owner) |
| 50 | +} |
| 51 | + |
| 52 | +class ProtectedAccessors extends MiniPhase { |
| 53 | + import ast.tpd._ |
| 54 | + import ProtectedAccessors._ |
| 55 | + |
| 56 | + override def phaseName = ProtectedAccessors.name |
| 57 | + |
| 58 | + object Accessors extends AccessProxies { |
| 59 | + def getterName = ProtectedGetterName |
| 60 | + def setterName = ProtectedSetterName |
| 61 | + |
| 62 | + val insert = new Insert { |
| 63 | + def needsAccessor(sym: Symbol)(implicit ctx: Context) = ProtectedAccessors.needsAccessor(sym) |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + override def prepareForAssign(tree: Assign)(implicit ctx: Context) = { |
| 68 | + tree.lhs match { |
| 69 | + case lhs: RefTree if needsAccessor(lhs.symbol) => lhs.putAttachment(LHS, ()) |
| 70 | + case _ => |
| 71 | + } |
| 72 | + ctx |
| 73 | + } |
| 74 | + |
| 75 | + private def isLHS(tree: RefTree) = tree.removeAttachment(LHS).isDefined |
| 76 | + |
| 77 | + override def transformIdent(tree: Ident)(implicit ctx: Context): Tree = |
| 78 | + if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree) |
| 79 | + |
| 80 | + override def transformSelect(tree: Select)(implicit ctx: Context): Tree = |
| 81 | + if (isLHS(tree)) tree else Accessors.insert.accessorIfNeeded(tree) |
| 82 | + |
| 83 | + override def transformAssign(tree: Assign)(implicit ctx: Context): Tree = |
| 84 | + Accessors.insert.accessorIfNeeded(tree) |
| 85 | + |
| 86 | + override def transformTemplate(tree: Template)(implicit ctx: Context): Tree = |
| 87 | + cpy.Template(tree)(body = Accessors.addAccessorDefs(tree.symbol.owner, tree.body)) |
| 88 | +} |
0 commit comments