-
Notifications
You must be signed in to change notification settings - Fork 1.1k
new TASTy header format - add experimental version and tooling version fields #11343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 0 additions & 46 deletions
46
tasty/src-bootstrapped/dotty/tools/tasty/TastyHeaderUnpickler.scala
This file was deleted.
Oops, something went wrong.
29 changes: 0 additions & 29 deletions
29
tasty/src-non-bootstrapped/dotty/tools/tasty/TastyHeaderUnpickler.scala
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package dotty.tools.tasty | ||
|
||
import java.util.UUID | ||
|
||
import TastyFormat.{MajorVersion, MinorVersion, ExperimentalVersion, header} | ||
|
||
/** | ||
* The Tasty Header consists of four fields: | ||
* - uuid | ||
* - contains a hash of the sections of the TASTy file | ||
* - majorVersion | ||
* - matching the TASTy format version that last broke backwards compatibility | ||
* - minorVersion | ||
* - matching the TASTy format version that last broke forward compatibility | ||
* - experimentalVersion | ||
* - 0 for final compiler version | ||
* - positive for between minor versions and forward compatibility | ||
* is broken since the previous stable version. | ||
* - toolingVersion | ||
* - arbitrary string representing the tooling that produced the TASTy | ||
*/ | ||
sealed abstract case class TastyHeader( | ||
uuid: UUID, | ||
majorVersion: Int, | ||
minorVersion: Int, | ||
experimentalVersion: Int, | ||
toolingVersion: String // String could lead to a lot of duplication, perhaps cache headers? | ||
) | ||
|
||
class TastyHeaderUnpickler(reader: TastyReader) { | ||
import TastyHeaderUnpickler._ | ||
import reader._ | ||
|
||
def this(bytes: Array[Byte]) = this(new TastyReader(bytes)) | ||
|
||
/** reads and verifies the TASTy version, extracting the UUID */ | ||
def readHeader(): UUID = | ||
readFullHeader().uuid | ||
|
||
/** reads and verifies the TASTy version, extracting the whole header */ | ||
def readFullHeader(): TastyHeader = { | ||
|
||
for (i <- 0 until header.length) | ||
check(readByte() == header(i), "not a TASTy file") | ||
val fileMajor = readNat() | ||
if (fileMajor <= 27) { // old behavior before `tasty-core` 3.0.0-M4 | ||
val fileMinor = readNat() | ||
val signature = signatureString(expectedMinor, fileMajor, fileMinor.toString) | ||
throw new UnpickleException(signature + backIncompatAddendum + myAddendum) | ||
} | ||
else { | ||
val fileMinor = readNat() | ||
val fileExperimental = readNat() | ||
val toolingVersion = { | ||
val length = readNat() | ||
val start = currentAddr | ||
val end = start + length | ||
goto(end) | ||
new String(bytes, start.index, length) // NOTE: new string object here (should probably cache as it leaks) | ||
bishabosha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
val validVersion = ( // inlined from `dotty.tools.tasty.TastyVersionFormatTest.TastyVersion.<:<` | ||
bishabosha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fileMajor == MajorVersion && ( | ||
if (fileExperimental == ExperimentalVersion) { | ||
if (ExperimentalVersion == 0) { | ||
fileMinor <= MinorVersion | ||
} | ||
else { | ||
fileMinor == MinorVersion | ||
} | ||
} | ||
else { | ||
fileExperimental == 0 && fileMinor < MinorVersion | ||
} | ||
) | ||
bishabosha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
check(validVersion, { | ||
val foundMinor = showMinorVersion(fileMinor, fileExperimental) | ||
val producedByAddendum = s"\nThe TASTy file was produced by $toolingVersion.$myAddendum" | ||
val signature = signatureString(expectedMinor, fileMajor, foundMinor) | ||
if (fileExperimental != 0) | ||
signature + unstableAddendum + producedByAddendum | ||
else if (fileMajor < MajorVersion) | ||
signature + backIncompatAddendum + producedByAddendum | ||
else | ||
signature + forwardIncompatAddendum + producedByAddendum | ||
} | ||
) | ||
val uuid = new UUID(readUncompressedLong(), readUncompressedLong()) | ||
new TastyHeader(uuid, fileMajor, fileMinor, fileExperimental, toolingVersion) {} | ||
} | ||
} | ||
|
||
def isAtEnd: Boolean = reader.isAtEnd | ||
|
||
private def check(cond: Boolean, msg: => String): Unit = { | ||
if (!cond) throw new UnpickleException(msg) | ||
} | ||
} | ||
|
||
object TastyHeaderUnpickler { | ||
|
||
private def showMinorVersion(min: Int, exp: Int) = { | ||
val expStr = if (exp == 0) "" else s" [unstable release: $exp]" | ||
s"$min$expStr" | ||
} | ||
|
||
private def expectedMinor = showMinorVersion(MinorVersion, ExperimentalVersion) | ||
|
||
private def myAddendum = { | ||
if (ExperimentalVersion > 0) | ||
"\nNote that your tooling is currently using an unstable TASTy version." | ||
else | ||
"" | ||
} | ||
|
||
private def signatureString(minorVersion: String, fileMajor: Int, fileMinor: String) = ( | ||
s"""TASTy signature has wrong version. | ||
| expected: {majorVersion: $MajorVersion, minorVersion: $minorVersion} | ||
| found : {majorVersion: $fileMajor, minorVersion: $fileMinor} | ||
| | ||
|""".stripMargin | ||
) | ||
|
||
private def unstableAddendum = | ||
"""This TASTy file was produced by an unstable release. | ||
|To read this TASTy file, your tooling must be at the same version.""".stripMargin | ||
|
||
private def backIncompatAddendum = | ||
"""This TASTy file was produced by a backwards incompatible release. | ||
bishabosha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|Please recompile this TASTy with a later version.""".stripMargin | ||
|
||
private def forwardIncompatAddendum = | ||
"""This TASTy file was produced by a forwards incompatible release. | ||
bishabosha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|To read this TASTy file, please upgrade your tooling.""".stripMargin | ||
} |
84 changes: 84 additions & 0 deletions
84
tasty/test/dotty/tools/tasty/TastyHeaderUnpicklerTest.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package dotty.tools.tasty | ||
|
||
import org.junit.Assert._ | ||
import org.junit.{Test, Ignore} | ||
|
||
import TastyFormat._ | ||
import TastyBuffer._ | ||
|
||
@Ignore // comment if you want to experiment with error messages | ||
class TastyHeaderUnpicklerTest { | ||
|
||
import TastyHeaderUnpicklerTest._ | ||
|
||
@Test def vanilla: Unit = { | ||
runTest(MajorVersion, MinorVersion, ExperimentalVersion, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345") | ||
} | ||
|
||
@Test def failBumpExperimental: Unit = { | ||
(runTest(MajorVersion, MinorVersion, ExperimentalVersion + 1, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345")) | ||
} | ||
|
||
@Test def failBumpMinor: Unit = { | ||
(runTest(MajorVersion, MinorVersion + 1, ExperimentalVersion, "Scala compiler 3.1.0-RC1")) | ||
} | ||
|
||
@Test def failBumpMajor: Unit = { | ||
(runTest(MajorVersion + 1, MinorVersion, ExperimentalVersion, "Scala compiler 4.0.0-M1")) | ||
} | ||
|
||
@Test def failBumpMajorFinal: Unit = { | ||
(runTest(MajorVersion + 1, MinorVersion, 0, "Scala compiler 4.0.0")) | ||
} | ||
|
||
@Test def okSubtractExperimental: Unit = { | ||
(runTest(MajorVersion, MinorVersion, ExperimentalVersion - 1, "Scala compiler 3.0.0")) | ||
} | ||
|
||
@Test def okSubtractMinor: Unit = { | ||
(runTest(MajorVersion, MinorVersion - 1, ExperimentalVersion, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345")) | ||
} | ||
|
||
@Test def failSubtractMajor: Unit = { | ||
(runTest(MajorVersion - 1, MinorVersion, ExperimentalVersion, "Scala compiler 3.0.0-M4-bin-SNAPSHOT-git-12345")) | ||
} | ||
|
||
} | ||
|
||
object TastyHeaderUnpicklerTest { | ||
|
||
|
||
def fillHeader(maj: Int, min: Int, exp: Int, compiler: String): TastyBuffer = { | ||
val compilerBytes = compiler.getBytes(java.nio.charset.StandardCharsets.UTF_8) | ||
val buf = new TastyBuffer(header.length + 32 + compilerBytes.length) | ||
for (ch <- header) buf.writeByte(ch.toByte) | ||
buf.writeNat(maj) | ||
buf.writeNat(min) | ||
buf.writeNat(exp) | ||
buf.writeNat(compilerBytes.length) | ||
buf.writeBytes(compilerBytes, compilerBytes.length) | ||
buf.writeUncompressedLong(237478l) | ||
buf.writeUncompressedLong(324789l) | ||
buf | ||
} | ||
|
||
def runTest(maj: Int, min: Int, exp: Int, compiler: String): Unit = { | ||
val headerBuffer = fillHeader(maj, min, exp, compiler) | ||
val bs = headerBuffer.bytes.clone | ||
|
||
val hr = new TastyHeaderUnpickler(bs) | ||
|
||
hr.readFullHeader() | ||
} | ||
|
||
def expectUnpickleError(op: => Unit) = { | ||
try { | ||
op | ||
fail() | ||
} | ||
catch { | ||
case err: UnpickleException => () | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.