summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypingTransformers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala160
-rw-r--r--test/files/jvm/protectedacc.check10
-rw-r--r--test/files/jvm/protectedacc.scala84
4 files changed, 251 insertions, 9 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
index c4de6f6fab..ffab99ed26 100644
--- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
+++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
@@ -6,6 +6,8 @@
package scala.tools.nsc.transform
+import scala.collection.mutable.{Map, HashMap}
+
/** A base class for transforms.
* A transform contains a compiler phase which applies a tree transformer.
*/
@@ -19,13 +21,17 @@ trait TypingTransformers {
analyzer.rootContext(unit, EmptyTree, true))
private var curTree: Tree = _
+ var typers: Map[Symbol, analyzer.Typer] = new HashMap
+
override def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans)
def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = {
val savedLocalTyper = localTyper
localTyper = localTyper.atOwner(tree, owner)
+ typers += owner -> localTyper;
val result = super.atOwner(owner)(trans)
localTyper = savedLocalTyper
+ typers.excl(owner);
result
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index 42c03ab884..776425e2b7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -21,7 +21,7 @@ import nsc.symtab.Flags._
* @author Martin Odersky
* @version 1.0
*/
-abstract class SuperAccessors extends transform.Transform {
+abstract class SuperAccessors extends transform.Transform with transform.TypingTransformers {
// inherits abstract value `global' and class `Phase' from Transform
import global._
@@ -34,7 +34,7 @@ abstract class SuperAccessors extends transform.Transform {
protected def newTransformer(unit: CompilationUnit): Transformer =
new SuperAccTransformer(unit)
- class SuperAccTransformer(unit: CompilationUnit) extends Transformer {
+ class SuperAccTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
private var validCurrentOwner = true
private var accDefs: List[Pair[Symbol, ListBuffer[Tree]]] = List()
@@ -62,11 +62,13 @@ abstract class SuperAccessors extends transform.Transform {
}
super.transform(tree)
case Template(parents, body) =>
- val ownAccDefs = new ListBuffer[Tree]
- accDefs = Pair(currentOwner, ownAccDefs) :: accDefs;
- val body1 = transformTrees(body)
- accDefs = accDefs.tail
- copy.Template(tree, parents, ownAccDefs.toList ::: body1)
+ val ownAccDefs = new ListBuffer[Tree];
+ accDefs = Pair(currentOwner, ownAccDefs) :: accDefs;
+// val body1 = transformTrees(body);
+ val Template(_, body1) = super.transform(tree);
+ accDefs = accDefs.tail;
+ copy.Template(tree, parents, ownAccDefs.toList ::: body1);
+
case Select(qual @ This(_), name) =>
val sym = tree.symbol
if ((sym hasFlag PARAMACCESSOR) && (sym.alias != NoSymbol)) {
@@ -78,7 +80,13 @@ abstract class SuperAccessors extends transform.Transform {
if (settings.debug.value)
System.out.println("alias replacement: " + tree + " ==> " + result);//debug
transform(result)
- } else tree
+ } else {
+ if (needsProtectedAccessor(sym, tree.pos)) {
+ if (settings.debug.value) log("Adding protected accessor for " + tree);
+ transform(makeAccessor(tree.asInstanceOf[Select]))
+ } else
+ tree
+ }
case Select(sup @ Super(_, mix), name) =>
val sym = tree.symbol
val clazz = sup.symbol
@@ -107,6 +115,27 @@ abstract class SuperAccessors extends transform.Transform {
Select(gen.mkAttributedThis(clazz), superAcc) setType tree.tpe;
}
} else tree
+
+ case Select(qual, name) =>
+ val sym = tree.symbol
+ if (needsProtectedAccessor(sym, tree.pos)) {
+ if (settings.debug.value) log("Adding protected accessor for tree: " + tree);
+ transform(makeAccessor(tree.asInstanceOf[Select]))
+ } else
+ super.transform(tree)
+
+ case Assign(lhs @ Select(qual, name), rhs) =>
+ if (lhs.symbol.isVariable &&
+ lhs.symbol.hasFlag(JAVA) &&
+ needsProtectedAccessor(lhs.symbol, tree.pos)) {
+ if (settings.debug.value) log("Adding protected setter for " + tree)
+ val setter = makeSetter(lhs);
+ if (settings.debug.value)
+ log("Replaced " + tree + " with " + setter);
+ transform(typed(Apply(setter, List(qual, rhs))))
+ } else
+ super.transform(tree)
+
case Apply(fn, args) =>
copy.Apply(tree, transform(fn), transformArgs(args, fn.tpe.paramTypes))
case Function(vparams, body) =>
@@ -118,7 +147,7 @@ abstract class SuperAccessors extends transform.Transform {
}
override def atOwner[A](owner: Symbol)(trans: => A): A = {
- if (owner.isClass) validCurrentOwner = true;
+ if (owner.isClass) validCurrentOwner = true
super.atOwner(owner)(trans)
}
@@ -129,5 +158,118 @@ abstract class SuperAccessors extends transform.Transform {
validCurrentOwner = prevValidCurrentOwner
result
}
+
+ /** Add a protected accessor, if needed, and return a tree that calls
+ * the accessor and returns the the same member. The result is already
+ * typed.
+ */
+ private def makeAccessor(tree: Select): Tree = {
+ val Select(qual, name) = tree
+ val sym = tree.symbol
+ val clazz = hostForAccessorOf(sym, currentOwner.enclClass)
+ assert(clazz != NoSymbol, sym)
+ if (settings.debug.value)
+ log("Decided for host class: " + clazz)
+ val accName = nme.protName(sym.originalName)
+ var protAcc = clazz.info.decl(accName)
+ if (protAcc == NoSymbol) {
+ val hasArgs = sym.tpe.paramTypes != Nil
+
+ protAcc = clazz.newMethod(tree.pos, nme.protName(sym.originalName))
+ .setInfo(MethodType(List(clazz.typeOfThis),
+ if (hasArgs)
+ MethodType(sym.tpe.paramTypes, sym.tpe.resultType)
+ else
+ sym.tpe.resultType))
+ clazz.info.decls.enter(protAcc);
+ val code = DefDef(protAcc, vparamss =>
+ vparamss.tail.foldRight(Select(gen.mkAttributedRef(vparamss.head.head), sym): Tree) (
+ (vparams, fun) => Apply(fun, (vparams map { v => Ident(v) } ))))
+ if (settings.debug.value)
+ log(code)
+ accDefBuf(clazz) += typers(clazz).typed(code)
+ }
+ var res: Tree = atPos(tree.pos) { Apply(Select(This(clazz), protAcc), List(qual)) }
+ if (settings.debug.value)
+ log("Replaced " + tree + " with " + res)
+ typer.typedOperator(res)
+ }
+
+ /** Add an accessor for field, if needed, and return a selection tree for it .
+ * The result is not typed.
+ */
+ private def makeSetter(tree: Select): Tree = {
+ val field = tree.symbol
+ val clazz = hostForAccessorOf(field, currentOwner.enclClass)
+ assert(clazz != NoSymbol, field)
+ if (settings.debug.value)
+ log("Decided for host class: " + clazz)
+ val accName = nme.protSetterName(field.originalName)
+ var protAcc = clazz.info.decl(accName)
+ if (protAcc == NoSymbol) {
+ protAcc = clazz.newMethod(field.pos, nme.protSetterName(field.originalName))
+ .setInfo(MethodType(List(clazz.typeOfThis, field.tpe), definitions.UnitClass.tpe));
+ clazz.info.decls.enter(protAcc)
+ val code = DefDef(protAcc, vparamss => {
+ val obj :: value :: Nil = vparamss.head;
+ atPos(tree.pos) {
+ Assign(
+ Select(Ident(obj), field.name),
+ Ident(value))
+ }
+ })
+ if (settings.debug.value)
+ log(code);
+ accDefBuf(clazz) += typers(clazz).typed(code)
+ }
+ var res: Tree = atPos(tree.pos) { Select(This(clazz), protAcc) }
+ res
+ }
+
+ /** Does `sym' need an accessor when accessed from `currentOwner'?
+ * A special case arises for classes with explicit self-types. If the
+ * self type is a Java class, and a protected accessor is needed, we issue
+ * an error. If the self type is a Scala class, we don't add an accessor.
+ *
+ * If the access happens inside a 'trait', access is more problematic since
+ * the implementation code is moved to an '$class' class which does not
+ * inherit anything. Since we can't (yet) add accessors for 'required'
+ * classes, this has to be signaled as error.
+ */
+ private def needsProtectedAccessor(sym: Symbol, pos: PositionType): Boolean = {
+ val res = /* settings.debug.value && */
+ ((sym hasFlag PROTECTED)
+ && (!(currentOwner.enclClass.thisSym isSubClass sym.owner))
+ && (enclPackage(sym.owner) != enclPackage(currentOwner)))
+
+ if (res) {
+ val host = hostForAccessorOf(sym, currentOwner.enclClass)
+ if (host.thisSym != host) {
+ if (host.thisSym.tpe.symbol.hasFlag(JAVA) || currentOwner.enclClass.isTrait)
+ unit.error(pos, "Implementation restriction: " + currentOwner.enclClass + " accesses protected "
+ + sym + " from 'required' " + host.thisSym.tpe)
+ false
+ } else res
+ } else res
+ }
+
+ /** Return the enclosing package of the given symbol. */
+ private def enclPackage(sym: Symbol): Symbol =
+ if ((sym == NoSymbol) || sym.isPackageClass) sym else enclPackage(sym.owner)
+
+ /** Return the innermost enclosing class C of referencingClass for which either
+ * of the following holds:
+ * - C is a subclass of sym.owner or
+ * - C is declared in the same package as sym's owner
+ */
+ private def hostForAccessorOf(sym: Symbol, referencingClass: Symbol): Symbol = {
+ if (referencingClass.isSubClass(sym.owner.enclClass)
+ || referencingClass.thisSym.isSubClass(sym.owner.enclClass)
+ || enclPackage(referencingClass) == enclPackage(sym.owner)) {
+ assert(referencingClass.isClass)
+ referencingClass
+ } else
+ hostForAccessorOf(sym, referencingClass.owner.enclClass)
+ }
}
}
diff --git a/test/files/jvm/protectedacc.check b/test/files/jvm/protectedacc.check
new file mode 100644
index 0000000000..632c9dabe7
--- /dev/null
+++ b/test/files/jvm/protectedacc.check
@@ -0,0 +1,10 @@
+10
+meth1(1) = 2
+meth2(1)(1) = 2
+10++ = 11
+count before: 3
+count after: 4
+10
+meth1(1) = 2
+meth2(1)(1) = 2
+10++ = 11
diff --git a/test/files/jvm/protectedacc.scala b/test/files/jvm/protectedacc.scala
new file mode 100644
index 0000000000..f54bb33d58
--- /dev/null
+++ b/test/files/jvm/protectedacc.scala
@@ -0,0 +1,84 @@
+//############################################################################
+// Test Java interaction with scala inner classes
+//############################################################################
+// $Id: $
+
+import java.io.{BufferedReader, File, FileWriter, InputStreamReader}
+
+/** The entry point of this test. It has to come first,
+ * before the package declarations. The parser wouldn't want it
+ * any other way.
+ */
+object Test {
+ def main(args: Array[String]): Unit = {
+ val b = new p.b.B;
+ val c = new b.C;
+ c.m
+
+ val ji = new p.b.JavaInteraction(Array('a', 'b', 'c'));
+ (new ji.Inner).m;
+
+ (new p.b.OuterObj.Inner).m
+ }
+}
+
+package p {
+ package a {
+
+ class A {
+ protected val x = 10;
+
+ protected def meth1(x: Int) = x + 1;
+ protected def meth2(x: Int)(y: Int) = x + y;
+
+ def getA: this.type = this;
+ }
+ }
+
+ package b {
+ import a._;
+
+ /** Test interraction with Scala inherited methods and currying. */
+ class B extends A {
+ class C {
+ def m = {
+ Console.println(x);
+ Console.println("meth1(1) = " + meth1(1));
+ Console.println("meth2(1)(1) = " + meth2(1)(1));
+
+ val inc = &meth2(1);
+ Console.println("10++ = " + inc(10));
+
+ getA.x;
+ }
+ }
+ }
+
+ /** Test interaction with Java inherited protected fields. */
+ class JavaInteraction(arr: Array[Char]) extends java.io.CharArrayReader(arr) {
+ class Inner {
+ def m = {
+ Console.println("count before: " + count);
+ count = count + 1;
+ Console.println("count after: " + count);
+ }
+ }
+ }
+
+ /** Test interaction when outer is an object. */
+ object OuterObj extends p.a.A {
+ class Inner {
+ def m = {
+ Console.println(x);
+ Console.println("meth1(1) = " + meth1(1));
+ Console.println("meth2(1)(1) = " + meth2(1)(1));
+
+ val inc = &meth2(1);
+ Console.println("10++ = " + inc(10));
+
+ getA.x;
+ }
+ }
+ }
+ }
+}