Skip to content

Commit

Permalink
Merge pull request #647 from dwijnand/ignore-experimental
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand authored Aug 23, 2021
2 parents 3dac7ba + 8a6fc57 commit b20f56d
Show file tree
Hide file tree
Showing 53 changed files with 2,085 additions and 197 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
name: ci

on:
pull_request:
push:
branches: ['main']
tags: ['v[0-9]']
tags: ['[0-9]']

jobs:
build:
Expand All @@ -17,7 +19,7 @@ jobs:
with:
java-version: ${{ matrix.java }}
- uses: coursier/cache-action@v6
- run: "sbt test mimaReportBinaryIssues 'set sbtplugin/scriptedSbt := \"1.2.8\"' 'scripted sbt-mima-plugin/minimal' IntegrationTest/test"
- run: "sbt test mimaReportBinaryIssues 'set sbtplugin/scriptedSbt := \"1.2.8\"' 'scripted sbt-mima-plugin/minimal'"
testFunctional:
needs: build
strategy:
Expand All @@ -42,3 +44,11 @@ jobs:
- uses: olafurpg/setup-scala@v13
- uses: coursier/cache-action@v6
- run: sbt "scripted sbt-mima-plugin/*${{ matrix.scripted }}"
testIntegration:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: olafurpg/setup-scala@v13
- uses: coursier/cache-action@v6
- run: sbt IntegrationTest/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.typesafe.tools.mima.core

private[mima] final case class AnnotInfo(name: String)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import java.lang.Double.longBitsToDouble
import java.nio.charset.StandardCharsets

/** Reads and interprets the bytes in a class file byte-buffer. */
private[core] sealed class BytesReader(buf: Array[Byte]) {
private[core] sealed abstract class BytesReader(buf: Array[Byte]) {
def pos: Int
def file: AbsFile

final def getByte(idx: Int): Byte = buf(idx)
final def getChar(idx: Int): Char = (((buf(idx) & 0xff) << 8) + (buf(idx + 1) & 0xff)).toChar

Expand All @@ -17,15 +20,17 @@ private[core] sealed class BytesReader(buf: Array[Byte]) {
final def getFloat(idx: Int): Float = intBitsToFloat(getInt(idx))
final def getDouble(idx: Int): Double = longBitsToDouble(getLong(idx))

//final def getString(idx: Int, len: Int): String = new java.io.DataInputStream(new java.io.ByteArrayInputStream(buf, idx, len)).readUTF
final def getString(idx: Int, len: Int): String = new String(buf, idx, len, StandardCharsets.UTF_8)

final def getBytes(idx: Int, bytes: Array[Byte]): Unit = System.arraycopy(buf, idx, bytes, 0, bytes.length)
}

/** A BytesReader which also holds a mutable pointer to where it will read next. */
private[core] final class BufferReader(buf: Array[Byte], val path: String) extends BytesReader(buf) {
private[core] final class BufferReader(val file: AbsFile) extends BytesReader(file.toByteArray) {
/** the buffer pointer */
var bp: Int = 0
def pos = bp

def nextByte: Byte = { val b = getByte(bp); bp += 1; b }
def nextChar: Char = { val c = getChar(bp); bp += 2; c } // Char = unsigned 2-bytes, aka u16
Expand All @@ -35,4 +40,10 @@ private[core] final class BufferReader(buf: Array[Byte], val path: String) exten
def acceptChar(exp: Char, ctx: => String = "") = { val obt = nextChar; assert(obt == exp, s"Expected $exp, obtained $obt$ctx"); obt }

def skip(n: Int): Unit = bp += n

def atIndex[T](i: Int)(body: => T): T = {
val saved = bp
bp = i
try body finally bp = saved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private[mima] sealed abstract class ClassInfo(val owner: PackageInfo) extends In
final var _methods: Members[MethodInfo] = NoMembers
final var _flags: Int = 0
final var _scopedPrivate: Boolean = false
final var _annotations: List[AnnotInfo] = Nil
final var _implClass: ClassInfo = NoClass
final var _moduleClass: ClassInfo = NoClass
final var _module: ClassInfo = NoClass
Expand All @@ -75,6 +76,7 @@ private[mima] sealed abstract class ClassInfo(val owner: PackageInfo) extends In
final def methods: Members[MethodInfo] = afterLoading(_methods)
final def flags: Int = afterLoading(_flags)
final def isScopedPrivate: Boolean = afterLoading(_scopedPrivate)
final def annotations: List[AnnotInfo] = afterLoading(_annotations)
final def implClass: ClassInfo = { owner.setImplClasses; _implClass } // returns NoClass if this is not a trait
final def moduleClass: ClassInfo = { owner.setModules; if (_moduleClass == NoClass) this else _moduleClass }
final def module: ClassInfo = { owner.setModules; if (_module == NoClass) this else _module }
Expand Down
18 changes: 6 additions & 12 deletions core/src/main/scala/com/typesafe/tools/mima/core/ClassPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@ import java.nio.file._

import scala.collection.JavaConverters._

private object AbsFile {
def apply(p: Path): AbsFile = {
val name = p.getFileName.toString.stripPrefix("/")
val path = p.toString.stripPrefix("/")
AbsFile(name)(path, () => Files.readAllBytes(p))
}
}

private[core] final case class AbsFile(name: String)(path: String, bytes: () => Array[Byte]) {
private[core] final case class AbsFile(name: String)(val jpath: Path) {
// Not defined as a simple wrapper of java.nio.file.Path, because Path#equals uses its FileSystem,
// differently to scala-reflect's AbstractFile, which breaks things like `distinct`.
def this(path: Path) = this(path.getFileName.toString.stripPrefix("/"))(path)

def toByteArray = bytes()
val path = jpath.toString.stripPrefix("/")
def toByteArray = Files.readAllBytes(jpath)
override def toString = path
}

Expand Down Expand Up @@ -74,7 +68,7 @@ private[mima] object ClassPath {

private final case class JrtCp(fs: FileSystem) extends ClassPath {
def packages(pkg: String) = packageToModules.keys.toStream.filter(pkgContains(pkg, _)).sorted
def classes(pkg: String) = packageToModules(pkg).flatMap(pkgClasses(_, pkg)).sortBy(_.toString).map(AbsFile(_))
def classes(pkg: String) = packageToModules(pkg).flatMap(pkgClasses(_, pkg)).sortBy(_.toString).map(new AbsFile(_))
def asClassPathString = fs.toString

private val packageToModules = listDir(fs.getPath("/packages"))
Expand All @@ -84,7 +78,7 @@ private[mima] object ClassPath {

private final case class PathCp(src: Path)(root: Path) extends ClassPath {
def packages(pkg: String) = listDir(pkgResolve(root, pkg)).filter(isPackage).map(pkgEntry(pkg, _))
def classes(pkg: String) = listDir(pkgResolve(root, pkg)).filter(isClass).map(AbsFile(_))
def classes(pkg: String) = listDir(pkgResolve(root, pkg)).filter(isClass).map(new AbsFile(_))
def asClassPathString = src.toString
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.typesafe.tools.mima.core

import java.io.IOException
import java.nio.file.Files

import ClassfileConstants._

Expand Down Expand Up @@ -56,14 +57,11 @@ final class ClassfileParser private (in: BufferReader, pool: ConstantPool) {
case ScalaSignatureATTR => isScala = true
case EnclosingMethodATTR => clazz._isLocalClass = true
case InnerClassesATTR => clazz._innerClasses = parseInnerClasses(clazz)
case TASTYATTR => parseTasty(clazz)
case _ =>
}
if (isScala) {
val end = in.bp
in.bp = runtimeAnnotStart
parsePickle(clazz)
in.bp = end
}
if (isScala)
in.atIndex(runtimeAnnotStart)(parsePickle(clazz))
}

private def parseMemberAttributes(member: MemberInfo) = {
Expand Down Expand Up @@ -100,7 +98,7 @@ final class ClassfileParser private (in: BufferReader, pool: ConstantPool) {
private def parsePickle(clazz: ClassInfo) = {
def parseScalaSigBytes() = {
in.acceptByte(STRING_TAG, s" for ${clazz.description}")
pool.getBytes(in.nextChar, in.bp)
pool.getBytes(in.nextChar)
}

def parseScalaLongSigBytes() = {
Expand All @@ -109,7 +107,7 @@ final class ClassfileParser private (in: BufferReader, pool: ConstantPool) {
in.acceptByte(STRING_TAG, s" for ${clazz.description}")
in.nextChar.toInt
}
pool.getBytes(entries.toList, in.bp)
pool.getBytes(entries.toList)
}

def checkScalaSigAnnotArg() = {
Expand Down Expand Up @@ -142,7 +140,14 @@ final class ClassfileParser private (in: BufferReader, pool: ConstantPool) {
}
i += 1
}
MimaUnpickler.unpickleClass(new PickleBuffer(bytes), clazz, in.path)
MimaUnpickler.unpickleClass(new PickleBuffer(bytes), clazz, in.file.path)
}

private def parseTasty(clazz: ClassInfo) = {
// TODO: sanity check UUIDs
val tpath = pool.file.jpath.resolveSibling(pool.file.name.stripSuffix(".class") + ".tasty")
val bytes = Files.readAllBytes(tpath)
TastyUnpickler.unpickleClass(new TastyReader(bytes), clazz, tpath.toString)
}

private final val ScalaSignatureAnnot = "Lscala.reflect.ScalaSignature;"
Expand All @@ -154,12 +159,13 @@ final class ClassfileParser private (in: BufferReader, pool: ConstantPool) {
private final val RuntimeAnnotationATTR = "RuntimeVisibleAnnotations"
private final val ScalaSignatureATTR = "ScalaSig"
private final val SignatureATTR = "Signature"
private final val TASTYATTR = "TASTY"
}

object ClassfileParser {
private[core] def parseInPlace(clazz: ClassInfo, file: AbsFile): Unit = {
val in = new BufferReader(file.toByteArray, file.toString)
parseHeader(in, file.toString)
val in = new BufferReader(file)
parseHeader(in, file)
val pool = ConstantPool.parseNew(clazz.owner.definitions, in)
val parser = new ClassfileParser(in, pool)
parser.parseClass(clazz)
Expand All @@ -176,7 +182,7 @@ object ClassfileParser {
def isSynthetic(flags: Int) = 0 != (flags & JAVA_ACC_SYNTHETIC)
def isAnnotation(flags: Int) = 0 != (flags & JAVA_ACC_ANNOTATION)

private def parseHeader(in: BufferReader, file: String) = {
private def parseHeader(in: BufferReader, file: AbsFile) = {
val magic = in.nextInt
if (magic != JAVA_MAGIC)
throw new IOException(
Expand Down
60 changes: 27 additions & 33 deletions core/src/main/scala/com/typesafe/tools/mima/core/ConstantPool.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@ private[core] object ConstantPool {
starts(i) = in.bp
i += 1
(in.nextByte.toInt: @switch) match {
case CONSTANT_UTF8 | CONSTANT_UNICODE => in.skip(in.nextChar)
case CONSTANT_CLASS | CONSTANT_STRING
| CONSTANT_METHODTYPE
| CONSTANT_MODULE | CONSTANT_PACKAGE => in.skip(2)
case CONSTANT_METHODHANDLE => in.skip(3)
case CONSTANT_INTEGER | CONSTANT_FLOAT
| CONSTANT_FIELDREF | CONSTANT_METHODREF
| CONSTANT_INTFMETHODREF
| CONSTANT_NAMEANDTYPE
| CONSTANT_INVOKEDYNAMIC => in.skip(4)
case CONSTANT_LONG | CONSTANT_DOUBLE => in.skip(8); i += 1
case tag => errorBadTag(tag, in.bp - 1)
case CONSTANT_UTF8 | CONSTANT_UNICODE => in.skip(in.nextChar)
case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE => in.skip(2)
case CONSTANT_MODULE | CONSTANT_PACKAGE => in.skip(2)
case CONSTANT_METHODHANDLE => in.skip(3)
case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF => in.skip(4)
case CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => in.skip(4)
case CONSTANT_INVOKEDYNAMIC => in.skip(4)
case CONSTANT_LONG | CONSTANT_DOUBLE => in.skip(8); i += 1
case tag => errorBadTag(tag, in.bp - 1)
}
}
new ConstantPool(definitions, in, starts)
Expand All @@ -43,6 +40,8 @@ private[core]
final class ConstantPool private (definitions: Definitions, in: BytesReader, starts: Array[Int]) {
import ConstantPool._, ClassInfo.ObjectClass

def file: AbsFile = in.file

private val length = starts.length
private val values = new Array[AnyRef](length)
private val internalized = new Array[String](length)
Expand Down Expand Up @@ -76,35 +75,30 @@ final class ConstantPool private (definitions: Definitions, in: BytesReader, sta

def getSuperClass(index: Int): ClassInfo = if (index == 0) ObjectClass else getClassInfo(index)

def getBytes(index: Int, pos: Int): Array[Byte] = {
if (index <= 0 || length <= index) errorBadIndex(index, pos: Int)
def getBytes(index: Int): Array[Byte] = {
if (index <= 0 || length <= index) errorBadIndex(index, in.pos)
else values(index) match {
case xs: Array[Byte] => xs
case _ =>
val start = firstExpecting(index, CONSTANT_UTF8)
val len = in.getChar(start).toInt
val bytes = new Array[Byte](len)
in.getBytes(start + 2, bytes)
recordAtIndex(getSubArray(bytes), index)
case _ => recordAtIndex(getSubArray(readBytes(index)), index)
}
}

def getBytes(indices: List[Int], pos: Int): Array[Byte] = {
val head = indices.head
values(head) match {
def getBytes(indices: List[Int]): Array[Byte] = {
for (index <- indices) if (index <= 0 || length <= index) errorBadIndex(index, in.pos)
val index = indices.head
values(index) match {
case xs: Array[Byte] => xs
case _ =>
val arr: Array[Byte] = indices.toArray.flatMap { index =>
if (index <= 0 || length <= index) errorBadIndex(index, pos)
val start = firstExpecting(index, CONSTANT_UTF8)
val result = new Array[Byte](in.getChar(start).toInt)
in.getBytes(start + 2, result)
result
}
recordAtIndex(getSubArray(arr), head)
case _ => recordAtIndex(getSubArray(indices.flatMap(readBytes).toArray), index)
}
}

private def readBytes(index: Int) = {
val start = firstExpecting(index, CONSTANT_UTF8)
val bytes = new Array[Byte](in.getChar(start).toInt)
in.getBytes(start + 2, bytes)
bytes
}

private def indexedOrUpdate[A <: AnyRef, R <: A](arr: Array[A], index: Int)(mk: => R): R = {
if (index <= 0 || index >= length)
throw new RuntimeException(s"bad constant pool index: $index, length: $length")
Expand All @@ -120,7 +114,7 @@ final class ConstantPool private (definitions: Definitions, in: BytesReader, sta
val start = starts(index)
val tag = in.getByte(start).toInt
if (tag == expectedTag) start + 1
else ConstantPool.errorBadTag(tag, start)
else errorBadTag(tag, start)
}

private def getSubArray(bytes: Array[Byte]): Array[Byte] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ private[mima] final class FieldInfo(owner: ClassInfo, bytecodeName: String, flag
private[mima] final class MethodInfo(owner: ClassInfo, bytecodeName: String, flags: Int, descriptor: String)
extends MemberInfo(owner, bytecodeName, flags, descriptor)
{
final var _annotations: List[AnnotInfo] = Nil
final def annotations: List[AnnotInfo] = _annotations

def methodString: String = s"$shortMethodString in ${owner.classString}"
def shortMethodString: String = {
val prefix = if (hasSyntheticName) if (isExtensionMethod) "extension " else "synthetic " else ""
Expand Down
Loading

0 comments on commit b20f56d

Please sign in to comment.