mirror of https://github.com/sbt/sbt.git
Hash of traits: include private fields, objects and super accessors
This commit is contained in:
parent
edc1174468
commit
6576844698
|
|
@ -261,7 +261,7 @@ object ClassToAPI {
|
||||||
def modifiers(i: Int): api.Modifiers =
|
def modifiers(i: Int): api.Modifiers =
|
||||||
{
|
{
|
||||||
import Modifier.{ isAbstract, isFinal }
|
import Modifier.{ isAbstract, isFinal }
|
||||||
new api.Modifiers(isAbstract(i), false, isFinal(i), false, false, false, false)
|
new api.Modifiers(isAbstract(i), false, isFinal(i), false, false, false, false, false)
|
||||||
}
|
}
|
||||||
def access(i: Int, pkg: Option[String]): api.Access =
|
def access(i: Int, pkg: Option[String]): api.Access =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ object APIUtil {
|
||||||
}
|
}
|
||||||
val byteToModifiers = (b: Byte) => {
|
val byteToModifiers = (b: Byte) => {
|
||||||
def x(bit: Int) = (b & (1 << bit)) != 0
|
def x(bit: Int) = (b & (1 << bit)) != 0
|
||||||
new Modifiers(x(0), x(1), x(2), x(3), x(4), x(5), x(6))
|
new Modifiers(x(0), x(1), x(2), x(3), x(4), x(5), x(6), x(7))
|
||||||
}
|
}
|
||||||
|
|
||||||
def isScalaSourceName(name: String): Boolean = name.endsWith(".scala")
|
def isScalaSourceName(name: String): Boolean = name.endsWith(".scala")
|
||||||
|
|
|
||||||
|
|
@ -151,17 +151,25 @@ final class HashAPI(includePrivate: Boolean, includeParamNames: Boolean, include
|
||||||
|
|
||||||
def hashDefinitions(ds: Seq[Definition], topLevel: Boolean, isTrait: Boolean): Unit =
|
def hashDefinitions(ds: Seq[Definition], topLevel: Boolean, isTrait: Boolean): Unit =
|
||||||
{
|
{
|
||||||
// If the enclosing definition is a trait, then we must include private vars and vals in the API hash
|
def isPublic(d: Definition): Boolean = d.access match { case _: xsbti.api.Public => true; case _ => false }
|
||||||
// of the trait, because scalac will generate setters, getters and fields for them in the traits
|
def isTraitBreaker(d: Definition): Boolean = d match {
|
||||||
// implementors.
|
// Vars and vals in traits introduce getters, setters and fields in the implementing classes.
|
||||||
val traitPrivateFields =
|
// See test `source-dependencies/trait-private-var
|
||||||
|
case _: FieldLike => true
|
||||||
|
// Objects in traits introduce fields in the implementing classes.
|
||||||
|
// See test `source-dependencies/trait-private-object`
|
||||||
|
case cl: ClassLike => cl.definitionType == DefinitionType.Module
|
||||||
|
// super calls introduce accessors that are not part of the public API
|
||||||
|
case d: Def => d.modifiers.isSuperAccessor
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
val includedPrivateDefinitions =
|
||||||
if (!includePrivate && !topLevel && isTrait) {
|
if (!includePrivate && !topLevel && isTrait) {
|
||||||
def isPublic(d: Definition): Boolean = d.access match { case _: xsbti.api.Public => true; case _ => false }
|
ds filter (x => isTraitBreaker(x) && !isPublic(x))
|
||||||
ds.collect { case fl: FieldLike if !isPublic(fl) => fl }
|
|
||||||
} else Seq.empty
|
} else Seq.empty
|
||||||
|
|
||||||
val defs = SameAPI.filterDefinitions(ds, topLevel, includePrivate)
|
val defs = SameAPI.filterDefinitions(ds, topLevel, includePrivate)
|
||||||
hashSymmetric(traitPrivateFields ++ defs, hashDefinition)
|
hashSymmetric(includedPrivateDefinitions ++ defs, hashDefinition)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -176,8 +176,6 @@ class NameHashingSpecification extends Specification {
|
||||||
* for the private vars of trait Foo in its implementor. If the clients of trait Foo are not recompiled,
|
* for the private vars of trait Foo in its implementor. If the clients of trait Foo are not recompiled,
|
||||||
* we get abstract method errors at runtime, because the types expected by the setter (for instance) does not
|
* we get abstract method errors at runtime, because the types expected by the setter (for instance) does not
|
||||||
* match.
|
* match.
|
||||||
*
|
|
||||||
* NOTE: This logic is important for vars and vals only. No other private member needs to be included.
|
|
||||||
*/
|
*/
|
||||||
"private var in traits are included in API hash" in {
|
"private var in traits are included in API hash" in {
|
||||||
/* trait Foo { private var x } */
|
/* trait Foo { private var x } */
|
||||||
|
|
@ -219,7 +217,28 @@ class NameHashingSpecification extends Specification {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"private def in traits are not included in API hash" in {
|
"private objects in traits are included in API hash" in {
|
||||||
|
/* trait Foo { private object x } */
|
||||||
|
val fooTrait1 =
|
||||||
|
simpleTrait("Foo",
|
||||||
|
simpleStructure(
|
||||||
|
new ClassLike(DefinitionType.Module, lzy(emptyType), lzy(simpleStructure()), Array.empty, Array.empty, "x", privateAccess, defaultModifiers, Array.empty)),
|
||||||
|
publicAccess)
|
||||||
|
|
||||||
|
/* trait Foo */
|
||||||
|
val fooTrait2 =
|
||||||
|
simpleTrait("Foo",
|
||||||
|
simpleStructure(),
|
||||||
|
publicAccess)
|
||||||
|
|
||||||
|
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
|
||||||
|
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
|
||||||
|
|
||||||
|
HashAPI(api1) !== HashAPI(api2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
"private non-synthetic def in traits are not included in API hash" in {
|
||||||
/* trait Foo { private def x } */
|
/* trait Foo { private def x } */
|
||||||
val fooTrait1 =
|
val fooTrait1 =
|
||||||
simpleTrait("Foo",
|
simpleTrait("Foo",
|
||||||
|
|
@ -239,6 +258,27 @@ class NameHashingSpecification extends Specification {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"private synthetic def in traits are included in API hash" in {
|
||||||
|
/* trait Foo { private <superaccessor> def x } */
|
||||||
|
val modifiers = new xsbti.api.Modifiers(false, false, false, false, false, false, false, true)
|
||||||
|
val fooTrait1 =
|
||||||
|
simpleTrait("Foo",
|
||||||
|
simpleStructure(new Def(Array.empty, emptyType, Array.empty, "x", privateAccess, modifiers, Array.empty)),
|
||||||
|
publicAccess)
|
||||||
|
|
||||||
|
/* trait Foo */
|
||||||
|
val fooTrait2 =
|
||||||
|
simpleTrait("Foo",
|
||||||
|
simpleStructure(),
|
||||||
|
publicAccess)
|
||||||
|
|
||||||
|
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
|
||||||
|
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
|
||||||
|
|
||||||
|
HashAPI(api1) !== HashAPI(api2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
"private types in traits are included not in API hash" in {
|
"private types in traits are included not in API hash" in {
|
||||||
/* trait Foo { private type x } */
|
/* trait Foo { private type x } */
|
||||||
val fooTrait1 =
|
val fooTrait1 =
|
||||||
|
|
@ -262,7 +302,7 @@ class NameHashingSpecification extends Specification {
|
||||||
/**
|
/**
|
||||||
* Checks that private vars in non-top-level traits are included as well.
|
* Checks that private vars in non-top-level traits are included as well.
|
||||||
*/
|
*/
|
||||||
"private members in nested traits" in {
|
"private variables in nested traits are include in the API hash" in {
|
||||||
/* class A { trait Foo { private var x } } */
|
/* class A { trait Foo { private var x } } */
|
||||||
val classA1 =
|
val classA1 =
|
||||||
simpleClass("A",
|
simpleClass("A",
|
||||||
|
|
@ -287,7 +327,7 @@ class NameHashingSpecification extends Specification {
|
||||||
/**
|
/**
|
||||||
* Checks that private traits are NOT included in the hash.
|
* Checks that private traits are NOT included in the hash.
|
||||||
*/
|
*/
|
||||||
"private traits" in {
|
"private inner traits are not included in the API hash" in {
|
||||||
/* class Foo { private trait T { private var x } } */
|
/* class Foo { private trait T { private var x } } */
|
||||||
val classFoo1 =
|
val classFoo1 =
|
||||||
simpleClass("Foo",
|
simpleClass("Foo",
|
||||||
|
|
@ -397,6 +437,6 @@ class NameHashingSpecification extends Specification {
|
||||||
private val strTpe = new Projection(emptyType, "String")
|
private val strTpe = new Projection(emptyType, "String")
|
||||||
private val publicAccess = new Public
|
private val publicAccess = new Public
|
||||||
private val privateAccess = new Private(new Unqualified)
|
private val privateAccess = new Private(new Unqualified)
|
||||||
private val defaultModifiers = new Modifiers(false, false, false, false, false, false, false)
|
private val defaultModifiers = new Modifiers(false, false, false, false, false, false, false, false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ object TestCaseGenerators {
|
||||||
private[this] def makeDefinition(name: String): Definition =
|
private[this] def makeDefinition(name: String): Definition =
|
||||||
new ClassLike(DefinitionType.ClassDef, lzy(new EmptyType()),
|
new ClassLike(DefinitionType.ClassDef, lzy(new EmptyType()),
|
||||||
lzy(new Structure(lzy(Array()), lzy(Array()), lzy(Array()))), Array(), Array(),
|
lzy(new Structure(lzy(Array()), lzy(Array()), lzy(Array()))), Array(), Array(),
|
||||||
name, new Public(), new Modifiers(false, false, false, false, false, false, false), Array())
|
name, new Public(), new Modifiers(false, false, false, false, false, false, false, false), Array())
|
||||||
|
|
||||||
private[this] def lzy[T <: AnyRef](x: T) = SafeLazy.strict(x)
|
private[this] def lzy[T <: AnyRef](x: T) = SafeLazy.strict(x)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -438,7 +438,7 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
||||||
val absOver = s.hasFlag(ABSOVERRIDE)
|
val absOver = s.hasFlag(ABSOVERRIDE)
|
||||||
val abs = s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED) || absOver
|
val abs = s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED) || absOver
|
||||||
val over = s.hasFlag(OVERRIDE) || absOver
|
val over = s.hasFlag(OVERRIDE) || absOver
|
||||||
new xsbti.api.Modifiers(abs, over, s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), hasMacro(s))
|
new xsbti.api.Modifiers(abs, over, s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), hasMacro(s), s.hasFlag(SUPERACCESSOR))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT)
|
private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT)
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ public final class Modifiers implements java.io.Serializable
|
||||||
private static final int ImplicitBit = 4;
|
private static final int ImplicitBit = 4;
|
||||||
private static final int LazyBit = 5;
|
private static final int LazyBit = 5;
|
||||||
private static final int MacroBit = 6;
|
private static final int MacroBit = 6;
|
||||||
|
private static final int SuperAccessorBit = 7;
|
||||||
|
|
||||||
private static int flag(boolean set, int bit)
|
private static int flag(boolean set, int bit)
|
||||||
{
|
{
|
||||||
return set ? (1 << bit) : 0;
|
return set ? (1 << bit) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy, boolean isMacro)
|
public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy, boolean isMacro, boolean isSuperAccessor)
|
||||||
{
|
{
|
||||||
this.flags = (byte)(
|
this.flags = (byte)(
|
||||||
flag(isAbstract, AbstractBit) |
|
flag(isAbstract, AbstractBit) |
|
||||||
|
|
@ -24,7 +25,8 @@ public final class Modifiers implements java.io.Serializable
|
||||||
flag(isSealed, SealedBit) |
|
flag(isSealed, SealedBit) |
|
||||||
flag(isImplicit, ImplicitBit) |
|
flag(isImplicit, ImplicitBit) |
|
||||||
flag(isLazy, LazyBit) |
|
flag(isLazy, LazyBit) |
|
||||||
flag(isMacro, MacroBit)
|
flag(isMacro, MacroBit) |
|
||||||
|
flag(isSuperAccessor, SuperAccessorBit)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +70,10 @@ public final class Modifiers implements java.io.Serializable
|
||||||
{
|
{
|
||||||
return flag(MacroBit);
|
return flag(MacroBit);
|
||||||
}
|
}
|
||||||
|
public final boolean isSuperAccessor()
|
||||||
|
{
|
||||||
|
return flag(SuperAccessorBit);
|
||||||
|
}
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
return (o instanceof Modifiers) && flags == ((Modifiers)o).flags;
|
return (o instanceof Modifiers) && flags == ((Modifiers)o).flags;
|
||||||
|
|
@ -78,6 +84,6 @@ public final class Modifiers implements java.io.Serializable
|
||||||
}
|
}
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy() + ", " + "isMacro: " + isMacro()+ ")";
|
return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy() + ", " + "isMacro: " + isMacro()+ ", isSuperAccessor:" + isSuperAccessor() + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
trait A {
|
||||||
|
val foo = 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
object B extends A {
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
println(foo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
trait A {
|
||||||
|
val foo = 0 + X.a
|
||||||
|
private object X { val a = 1 }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
> run
|
||||||
|
|
||||||
|
$ copy-file changes/A.scala A.scala
|
||||||
|
|
||||||
|
> run
|
||||||
Loading…
Reference in New Issue