Skip to content

Commit

Permalink
Merge pull request #143 from szeiger/issue/127-removing-inner-object
Browse files Browse the repository at this point in the history
Detect changes to inner modules
  • Loading branch information
szeiger committed Nov 30, 2016
2 parents e083f6d + 615b35a commit 014b33f
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 13 deletions.
12 changes: 9 additions & 3 deletions core/src/main/scala/com/typesafe/tools/mima/core/ClassInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ abstract class ClassInfo(val owner: PackageInfo) extends HasDeclarationName with
else owner.fullName + "." + bytecodeName
}

var _innerClasses: Seq[String] = Seq.empty
def innerClasses = { ensureLoaded(); _innerClasses }

var _isTopLevel = true
def isTopLevel = { ensureLoaded(); _isTopLevel }

final override def equals(other: Any): Boolean = other match {
case that: ClassInfo => (that canEqual this) && this.fullName == that.fullName
case _ => false
Expand All @@ -65,10 +71,10 @@ abstract class ClassInfo(val owner: PackageInfo) extends HasDeclarationName with

override def canEqual(other: Any) = other.isInstanceOf[ClassInfo]

def formattedFullName = formatClassName(if (isObject) fullName.init else fullName)
def formattedFullName = formatClassName(if (isModule) fullName.init else fullName)

def declarationPrefix = {
if (isObject) "object"
if (isModule) "object"
else if (isTrait) "trait"
else if (loaded && isInterface) "interface" // java interfaces and traits with no implementation methods
else "class"
Expand Down Expand Up @@ -290,7 +296,7 @@ abstract class ClassInfo(val owner: PackageInfo) extends HasDeclarationName with
ClassfileParser.isInterface(flags)
}

def isObject: Boolean = bytecodeName.endsWith("$")
def isModule: Boolean = bytecodeName.endsWith("$")

/** Is this class public? */
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,17 @@ abstract class ClassfileParser(definitions: Definitions) {
val attrNameIndex = in.nextChar
c.sourceFileName = pool.getName(attrNameIndex)
}
} else if (attrName == "InnerClasses") {
val entries = in.nextChar.toInt
c._innerClasses = (0 until entries).map { _ =>
val innerIndex, outerIndex, innerNameIndex = in.nextChar.toInt
in.skip(2)
if (innerIndex != 0 && outerIndex != 0 && innerNameIndex != 0) {
val n = pool.getClassName(innerIndex)
if (n == c.bytecodeName) c._isTopLevel = false // an inner class lists itself in InnerClasses
if (pool.getClassName(outerIndex) == c.bytecodeName) n else ""
} else ""
}.filterNot(_.isEmpty)
} else if (attrName == "Scala" || attrName == "ScalaSig") {
this.parsedClass.isScala = true
}
Expand Down
19 changes: 9 additions & 10 deletions core/src/main/scala/com/typesafe/tools/mima/core/PackageInfo.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.typesafe.tools.mima.core

import scala.annotation.tailrec
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.ClassPath
import collection.mutable
Expand Down Expand Up @@ -65,25 +66,23 @@ abstract class PackageInfo(val owner: PackageInfo) {

lazy val accessibleClasses: Set[ClassInfo] = {
/** Fixed point iteration for finding all accessible classes. */
def accessibleClassesUnder(prefix: Set[ClassInfo]): Set[ClassInfo] = {
val vclasses = (classes.valuesIterator filter (isAccessible(_, prefix))).toSet
if (vclasses.isEmpty) vclasses
else vclasses union accessibleClassesUnder(vclasses)
@tailrec
def accessibleClassesUnder(prefix: Set[ClassInfo], found: Set[ClassInfo]): Set[ClassInfo] = {
val vclasses = (classes.valuesIterator.filter(isAccessible(_, prefix))).toSet
if (vclasses.isEmpty) found
else accessibleClassesUnder(vclasses, vclasses union found)
}

def isAccessible(clazz: ClassInfo, prefix: Set[ClassInfo]) = {
def isReachable = {
if (clazz.isSynthetic) false
else {
val idx = clazz.decodedName.lastIndexOf("$")
if (idx < 0) prefix.isEmpty // class name contains no $
else prefix exists (_.decodedName == clazz.decodedName.substring(0, idx)) // prefix before dollar is an accessible class detected previously
}
else if (prefix.isEmpty) clazz.isTopLevel && !clazz.bytecodeName.contains("$$")
else prefix.exists(_.innerClasses contains clazz.bytecodeName)
}
clazz.isPublic && isReachable
}

accessibleClassesUnder(Set.empty)
accessibleClassesUnder(Set.empty, Set.empty)
}

/** All implementation classes of traits (classes that end in "$" followed by "class"). */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
object A#B does not have a correspondent in new version
method B()A#B# in class A does not have a correspondent in new version
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class A {
object B {
def foo: Int = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class A {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
object A#B does not have a correspondent in new version
object A#B#C does not have a correspondent in new version
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
object A {
object B {
def foo: Int = 2
object C
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
object A {
}

0 comments on commit 014b33f

Please sign in to comment.