Skip to content
Open
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package dotc
package core
package tasty

import dotty.tools.tasty.TastyFormat.{SOURCE, PositionsSection}
import dotty.tools.tasty.TastyBuffer
import dotty.tools.tasty.TastyFormat.{PositionsSection, SOURCE}
import dotty.tools.tasty.{PickleException, TastyBuffer}
import TastyBuffer.*

import ast.*
import Trees.WithLazyFields
import util.{SourceFile, NoSource}
import util.{NoSource, SourceFile}
import core.*
import Annotations.*, Decorators.*
import Annotations.*
import Decorators.*
import collection.mutable
import util.Spans.*
import reporting.Message
Expand Down Expand Up @@ -48,7 +48,7 @@ object PositionPickler:
val content = source.content()
buf.writeNat(content.count(_ == '\n') + 1) // number of lines
var lastIndex = content.indexOf('\n', 0)
buf.writeNat(lastIndex) // size of first line
buf.writeNat(if lastIndex == -1 then content.length else lastIndex) // size of first line
while lastIndex != -1 do
val nextIndex = content.indexOf('\n', lastIndex + 1)
val end = if nextIndex != -1 then nextIndex else content.length
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class TreeUnpickler(reader: TastyReader,
if (tag >= firstLengthTreeTag) goto(readEnd())
else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() }
else if (tag >= firstASTTreeTag) skipTree()
else if (tag >= firstNatTreeTag) readNat()
else if (tag >= firstNatTreeTag) readLongInt()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two calls used to be equivalent because we didn't check that it was really a nat or really not a long

}
def skipTree(): Unit = skipTree(readByte())

Expand Down Expand Up @@ -225,7 +225,7 @@ class TreeUnpickler(reader: TastyReader,
if (mode == MemberDefsOnly) skipTree(tag)
else if (tag >= firstLengthTreeTag) {
val end = readEnd()
var nrefs = numRefs(tag)
val nrefs = numRefs(tag)
if (nrefs < 0) {
for (i <- nrefs until 0) scanTree(buf)
goto(end)
Expand All @@ -241,7 +241,7 @@ class TreeUnpickler(reader: TastyReader,
}
else if (tag >= firstNatASTTreeTag) { readNat(); scanTree(buf) }
else if (tag >= firstASTTreeTag) scanTree(buf)
else if (tag >= firstNatTreeTag) readNat()
else if (tag >= firstNatTreeTag) readLongInt()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

}
}

Expand Down
59 changes: 59 additions & 0 deletions compiler/test/dotty/tools/dotc/core/tasty/TastyUnpickerTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package dotty.tools.dotc.core.tasty

import dotty.tools.io.VirtualFile
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.ast.*
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.tasty.UnpickleException
import org.junit.Test
import org.junit.Assert.*

class TastyUnpickerTest {
private def test(bytes: Int*): Unit =
val base = new ContextBase
given Context = base.initialCtx.fresh
val bytesArray = bytes.map(_.toByte).toArray
val dotty = new DottyUnpickler(
tastyFile = new VirtualFile("bytes.tasty", bytesArray),
bytes = bytesArray,
isBestEffortTasty = false
)
dotty.enter(roots = Set.empty)
dotty.rootTrees.foreach(t => t.foreachSubTree(identity))

// this used to go into an infinite loop as we could 'goto' a negative number when looking for a section end
@Test def regressionInfiniteLoop(): Unit =
assertThrows(classOf[UnpickleException], () => test(
0x5c, 0xa1, 0xab, 0x1f, 0x9c, 0x88, 0x80, 0xb8, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x20, 0x33, 0x2e, 0x38, 0x2e, 0x32, 0x2d,
0x52, 0x43, 0x31, 0x2d, 0x62, 0x69, 0x6e, 0x2d, 0x53, 0x4e, 0x41, 0xb0, 0xac, 0xb7, 0xa9, 0x54, 0x2d, 0x6e, 0x6f, 0x6e,
0x62, 0x9a, 0x90, 0x8b, 0x8c, 0x74, 0x72, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x6f, 0x6c, 0x77, 0x2e,
0x64, 0x6f, 0x74, 0x3c, 0x00, 0xd5, 0x86, 0x0a, 0xd0, 0x5b, 0x65, 0xe8, 0x00, 0xd4, 0x9d, 0xda, 0xab, 0x1f, 0x3b, 0x18,
0x02, 0xb6, 0x01, 0x84, 0x41, 0x53, 0x54, 0x73, 0x01, 0x85, 0x7a, 0x63, 0x61, 0x6c, 0x61, 0x01, 0x8a, 0x61, 0x6e, 0x6e,
0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x02, 0x82, 0x81, 0x82, 0x01, 0x8c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x93,
0x9a, 0x91, 0x8b, 0x61, 0x6c, 0x01, 0x88, 0x6c, 0x5d, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x01, 0x8f, 0x63, 0x61, 0x70,
0x74, 0x75, 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x01, 0x86, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c,
0x01, 0x86, 0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x8a, 0x41, 0x6e, 0x6e, 0x6f, 0x69, 0x61, 0x74, 0x74, 0x6f, 0x6e,
0x02, 0x82, 0x83, 0x89, 0x3f, 0x82, 0x88, 0x8a, 0x01, 0x90, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x6e, 0x6e, 0x6e,
0x69, 0x0b, 0x00, 0x6f, 0x6f, 0x61, 0x01, 0x84, 0x55, 0x6e, 0x69, 0x74, 0x01, 0x8a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x46, 0x69, 0x6c, 0x65, 0x01, 0x88, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x02, 0x82, 0x83, 0x8f, 0x02, 0x82,
0x90, 0x8e, 0x01, 0x84, 0x6a, 0x61, 0x76, 0x61, 0x01, 0x84, 0x6c, 0x61, 0x6e, 0x67, 0x02, 0x82, 0x92, 0x93, 0x01, 0x86,
0x53, 0x5d, 0x72, 0x69, 0x6c, 0x67, 0x02, 0x82, 0x94, 0x95, 0x3f, 0x83, 0x88, 0x91, 0x96, 0x01, 0xa9, 0x6c, 0x69, 0x62,
0x72, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x02, 0x82,
0x83, 0x84, 0x3f, 0x83, 0x88, 0x99, 0x96, 0x01, 0x9e, 0x75, 0x6e, 0x64, 0xa3, 0x8d, 0xdf, 0x8d, 0x9a, 0x89, 0x96, 0x9a,
0x77, 0x20, 0x61, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x53, 0x49, 0x50, 0x2d, 0x34, 0x36, 0x01,
0x89, 0x50, 0x6f, 0xd7, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x88, 0x41, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x01, 0x8a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x81, 0x74, 0x6d, 0x72, 0x80, 0xd5, 0x84, 0x83, 0x72, 0xdd, 0x2e, 0x47,
0xe1, 0x13, 0x24, 0x38, 0x40, 0x40, 0x81, 0x4b, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x86, 0x86,
0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0f, 0x0f, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x9e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x85, 0x02, 0x03, 0x04, 0x81, 0x98,
) )
}
3 changes: 3 additions & 0 deletions tasty/src/dotty/tools/tasty/PickleException.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package dotty.tools.tasty

class PickleException(msg: String) extends RuntimeException(msg)
17 changes: 12 additions & 5 deletions tasty/src/dotty/tools/tasty/TastyBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,18 @@ class TastyBuffer(initialSize: Int) {
length += n
}

/** Write a natural number in big endian format, base 128.
* All but the last digits have bit 0x80 set.
/** Write a natural number in big endian format, base 128, as octets.
* All octets but the last have bit 0x80 unset.
*/
def writeNat(x: Int): Unit =
def writeNat(x: Int): Unit = {
if (x < 0) {
throw new PickleException(s"Expected a natural (nonnegative) number to write, but got: $x")
}
writeLongNat(x.toLong & 0x00000000FFFFFFFFL)
}

/** Write a natural number in 2's complement big endian format, base 128.
* All but the last digits have bit 0x80 set.
/** Write a natural number in 2's complement big endian format, base 128, as octets.
* All octets but the last have bit 0x80 unset.
*/
def writeInt(x: Int): Unit =
writeLongInt(x)
Expand All @@ -83,6 +87,9 @@ class TastyBuffer(initialSize: Int) {
* Int.MAX_VALUE.
*/
def writeLongNat(x: Long): Unit = {
if (x < 0) {
throw new PickleException(s"Expected a natural (nonnegative) number to write, but got: $x")
}
def writePrefix(x: Long): Unit = {
val y = x >>> 7
if (y != 0L) writePrefix(y)
Expand Down
48 changes: 36 additions & 12 deletions tasty/src/dotty/tools/tasty/TastyReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int =
/** The address of the next byte to read */
def currentAddr: Addr = addr(bp)

/** the address one greater than the last brte to read */
/** the address one greater than the last byte to read */
def endAddr: Addr = addr(end)

/** Have all bytes been read? */
Expand Down Expand Up @@ -58,34 +58,55 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int =
result
}

/** Read a natural number fitting in an Int in big endian format, base 128.
* All but the last digits have bit 0x80 set.
/** Read a 31-bit natural (nonnegative) integer number in 2's complement big endian format, base 128, stored as octets.
* All octets but the last have bit 0x80 unset.
*/
def readNat(): Int = readLongNat().toInt
def readNat(): Int = {
val l = readLongNat()
if (l > Int.MaxValue) {
throw new UnpickleException(s"Expected a 31-bit nat, got: $l")
}
l.toInt
}

/** Read an integer number in 2's complement big endian format, base 128.
* All but the last digits have bit 0x80 set.
/** Read a 32-bit integer number in 2's complement big endian format, base 128, stored as octets.
* All octets but the last have bit 0x80 unset.
*/
def readInt(): Int = readLongInt().toInt
def readInt(): Int = {
val l = readLongInt()
if (l < Int.MinValue || l > Int.MaxValue) {
throw new UnpickleException(s"Expected a 32-bit int, got: $l")
}
l.toInt
}

/** Read a natural number fitting in a Long in big endian format, base 128.
* All but the last digits have bit 0x80 set.
/** Read a 63-bit natural (nonnegative) number in 2's complement big endian format, base 128, stored as octets.
* All octets but the last have bit 0x80 unset.
*/
def readLongNat(): Long = {
val ogBp = bp
var b = 0L
var x = 0L
while ({
b = bytes(bp)
x = (x << 7) | (b & 0x7f)
bp += 1
(b & 0x80) == 0
})
()
}) ()
if (bp - ogBp > 9) {
throw new UnpickleException(s"Expected a long nat, but read too many bytes (${bp - ogBp})")
}
if (x < 0) {
throw new UnpickleException(s"Expected a nat, got: $x")
}
x
}

/** Read a long integer number in 2's complement big endian format, base 128. */
/** Read a 64-bit long integer number in 2's complement big endian format, base 128, stored as octets.
* All octets but the last have bit 0x80 unset.
*/
def readLongInt(): Long = {
val ogBp = bp
var b = bytes(bp)
var x: Long = (b << 1).toByte >> 1 // sign extend with bit 6.
bp += 1
Expand All @@ -94,6 +115,9 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int =
x = (x << 7) | (b & 0x7f)
bp += 1
}
if (bp - ogBp > 10) {
throw new UnpickleException(s"Expected a long int, but read too many bytes (${bp - ogBp})")
}
x
}

Expand Down
Loading