Skip to content

Commit c81113c

Browse files
committed
Test -print-tasty output of Java sources
1 parent 373e8e9 commit c81113c

File tree

11 files changed

+259
-13
lines changed

11 files changed

+259
-13
lines changed

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

+1
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ private sealed trait YSettings:
388388
val YshowPrintErrors: Setting[Boolean] = BooleanSetting("-Yshow-print-errors", "Don't suppress exceptions thrown during tree printing.")
389389
val YprintTasty: Setting[Boolean] = BooleanSetting("-Yprint-tasty", "Prints the generated TASTY to stdout.")
390390
val YtestPickler: Setting[Boolean] = BooleanSetting("-Ytest-pickler", "Self-test for pickling functionality; should be used with -Ystop-after:pickler.")
391+
val YtestPicklerCheck: Setting[Boolean] = BooleanSetting("-Ytest-pickler-check", "Self-test for pickling -print-tasty output; should be used with -Ytest-pickler.")
391392
val YcheckReentrant: Setting[Boolean] = BooleanSetting("-Ycheck-reentrant", "Check that compiled program does not contain vars that can be accessed from a global root.")
392393
val YdropComments: Setting[Boolean] = BooleanSetting("-Ydrop-docs", "Drop documentation when scanning source files.", aliases = List("-Ydrop-comments"))
393394
val YcookComments: Setting[Boolean] = BooleanSetting("-Ycook-docs", "Cook the documentation (type check `@usecase`, etc.)", aliases = List("-Ycook-comments"))

compiler/src/dotty/tools/dotc/core/tasty/TastyAnsiiPrinter.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package dotty.tools.dotc
22
package core
33
package tasty
44

5-
class TastyAnsiiPrinter(bytes: Array[Byte]) extends TastyPrinter(bytes) {
5+
class TastyAnsiiPrinter(bytes: Array[Byte], testPickler: Boolean) extends TastyPrinter(bytes, testPickler) {
6+
7+
def this(bytes: Array[Byte]) = this(bytes, testPickler = false)
8+
69
override protected def nameStr(str: String): String = Console.MAGENTA + str + Console.RESET
710
override protected def treeStr(str: String): String = Console.YELLOW + str + Console.RESET
811
override protected def lengthStr(str: String): String = Console.CYAN + str + Console.RESET

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

+20-6
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ import dotty.tools.tasty.TastyBuffer.Addr
2020
object TastyPrinter:
2121

2222
def showContents(bytes: Array[Byte], noColor: Boolean): String =
23+
showContents(bytes, noColor, testPickler = false)
24+
25+
def showContents(bytes: Array[Byte], noColor: Boolean, testPickler: Boolean = false): String =
2326
val printer =
24-
if noColor then new TastyPrinter(bytes)
25-
else new TastyAnsiiPrinter(bytes)
27+
if noColor then new TastyPrinter(bytes, testPickler)
28+
else new TastyAnsiiPrinter(bytes, testPickler)
2629
printer.showContents()
2730

2831
def main(args: Array[String]): Unit = {
@@ -62,7 +65,9 @@ object TastyPrinter:
6265
println(line)
6366
}
6467

65-
class TastyPrinter(bytes: Array[Byte]) {
68+
class TastyPrinter(bytes: Array[Byte], val testPickler: Boolean) {
69+
70+
def this(bytes: Array[Byte]) = this(bytes, testPickler = false)
6671

6772
class TastyPrinterUnpickler extends TastyUnpickler(bytes) {
6873
var namesStart: Addr = uninitialized
@@ -84,9 +89,16 @@ class TastyPrinter(bytes: Array[Byte]) {
8489
private def printHeader(sb: StringBuilder): Unit =
8590
val header = unpickler.header
8691
sb.append("Header:\n")
87-
sb.append(s" version: ${header.majorVersion}.${header.minorVersion}.${header.experimentalVersion}\n")
88-
sb.append(" tooling: ").append(header.toolingVersion).append("\n")
89-
sb.append(" UUID: ").append(header.uuid).append("\n")
92+
if testPickler then
93+
// these fields are not stable when the TASTy/compiler versions change, so not useful for testing
94+
sb.append(" version: <elided>\n")
95+
sb.append(" tooling: <elided>\n")
96+
sb.append(" UUID: <elided>\n")
97+
else
98+
sb.append(s" version: ${header.majorVersion}.${header.minorVersion}.${header.experimentalVersion}\n")
99+
sb.append(" tooling: ").append(header.toolingVersion).append("\n")
100+
sb.append(" UUID: ").append(header.uuid).append("\n")
101+
end if
90102
sb.append("\n")
91103

92104
private def printNames(sb: StringBuilder): Unit =
@@ -230,6 +242,8 @@ class TastyPrinter(bytes: Array[Byte]) {
230242
import reader.*
231243
sb.append(s"\n\nAttributes (${reader.endAddr.index - reader.startAddr.index} bytes, starting from $base):\n")
232244
while !isAtEnd do
245+
// TODO: Should we elide attributes under testPickler? (i.e.
246+
// if we add new attributes many check files will need to be updated)
233247
val tag = readByte()
234248
sb.append(" ").append(attributeTagToString(tag))
235249
if isBooleanAttrTag(tag) then ()

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1085,10 +1085,10 @@ class TreeUnpickler(reader: TastyReader,
10851085
def complete(denot: SymDenotation)(using Context) =
10861086
denot.info = tdef.symbol.asType.info.subst(tparamRefs, derivedTparamRefs)
10871087
}
1088-
newSymbol(sym, tdef.name, Flags.JavaDefined | Flags.Param, completer, coord = cls.coord)
1088+
newSymbol(sym, tdef.name, Flags.JavaDefined | Flags.Param, completer, coord = coordAt(start))
10891089
lazy val derivedTparamRefs: List[Type] = derivedTparamSyms.map(_.typeRef)
10901090
val vparamSym =
1091-
newSymbol(sym, nme.syntheticParamName(1), pflags, defn.UnitType, coord = cls.coord)
1091+
newSymbol(sym, nme.syntheticParamName(1), pflags, defn.UnitType, coord = coordAt(start))
10921092
val vparamSymss: List[List[Symbol]] = List(vparamSym) :: Nil
10931093
val paramSymss =
10941094
if derivedTparamSyms.nonEmpty then derivedTparamSyms :: vparamSymss else vparamSymss

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

+1
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,7 @@ object JavaParsers {
894894
parents = superclass :: interfaces,
895895
stats = canonicalConstructor :: accessors ::: body,
896896
tparams = tparams,
897+
// ctorSpan = getSpan(start, nameOffset),
897898
needsDummyConstr = true
898899
)
899900
).withMods(mods.withFlags(Flags.JavaDefined | Flags.Final))

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
10101010
var modsText = modText(constr.mods, constr.symbol, "", isType = false)
10111011
if (!modsText.isEmpty) modsText = " " ~ modsText
10121012
if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this"
1013-
withEnclosingDef(constr) { addParamssText(tparamsTxt ~~ modsText, constr.trailingParamss) }
1013+
val ctorParamss =
1014+
// for fake `(x$1: Unit): Foo` constructor, don't print the param (span is not reconstructed correctly)
1015+
if constr.symbol.isAllOf(JavaParsers.fakeFlags) then Nil else constr.trailingParamss
1016+
withEnclosingDef(constr) { addParamssText(tparamsTxt ~~ modsText, ctorParamss) }
10141017
}
10151018
val parentsText = Text(impl.parents.map(constrText), if (ofNew) keywordStr(" with ") else ", ")
10161019
val derivedText = Text(impl.derived.map(toText(_)), ", ")

compiler/src/dotty/tools/dotc/transform/Pickler.scala

+34-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class Pickler extends Phase {
6969

7070
// Maps that keep a record if -Ytest-pickler is set.
7171
private val beforePickling = new mutable.HashMap[ClassSymbol, String]
72+
private val beforePicklingPrinted = new mutable.HashMap[ClassSymbol, String]
7273
private val pickledBytes = new mutable.HashMap[ClassSymbol, (CompilationUnit, Array[Byte])]
7374

7475
/** Drop any elements of this list that are linked module classes of other elements in the list */
@@ -184,7 +185,10 @@ class Pickler extends Phase {
184185
else
185186
val pickled = computePickled()
186187
reportPositionWarnings()
187-
if ctx.settings.YtestPickler.value then pickledBytes(cls) = (unit, pickled)
188+
if ctx.settings.YtestPickler.value then
189+
pickledBytes(cls) = (unit, pickled)
190+
if ctx.settings.YtestPicklerCheck.value then
191+
beforePicklingPrinted(cls) = TastyPrinter.showContents(pickled, noColor = true, testPickler = true)
188192
() => pickled
189193

190194
unit.pickled += (cls -> demandPickled)
@@ -251,15 +255,22 @@ class Pickler extends Phase {
251255
private def testUnpickler(using Context): Unit =
252256
pickling.println(i"testing unpickler at run ${ctx.runId}")
253257
ctx.initialize()
258+
val resolveCheck = ctx.settings.YtestPicklerCheck.value
254259
val unpicklers =
255260
for ((cls, (unit, bytes)) <- pickledBytes) yield {
256261
val unpickler = new DottyUnpickler(unit.source.file, bytes)
257262
unpickler.enter(roots = Set.empty)
258-
cls -> (unit, unpickler)
263+
val optCheck =
264+
if resolveCheck then
265+
val resolved = unit.source.file.resolveSibling(s"${cls.name.mangledString}.tastycheck")
266+
if resolved == null then None
267+
else Some(resolved)
268+
else None
269+
cls -> (unit, unpickler, optCheck)
259270
}
260271
pickling.println("************* entered toplevel ***********")
261272
val rootCtx = ctx
262-
for ((cls, (unit, unpickler)) <- unpicklers) do
273+
for ((cls, (unit, unpickler, optCheck)) <- unpicklers) do
263274
val testJava = unit.typedAsJava
264275
if testJava then
265276
if unpickler.unpickler.nameAtRef.contents.exists(_ == nme.FromJavaObject) then
@@ -268,6 +279,15 @@ class Pickler extends Phase {
268279
val freshUnit = CompilationUnit(rootCtx.compilationUnit.source)
269280
freshUnit.needsCaptureChecking = unit.needsCaptureChecking
270281
freshUnit.knowsPureFuns = unit.knowsPureFuns
282+
optCheck match
283+
case Some(check) =>
284+
import java.nio.charset.StandardCharsets.UTF_8
285+
val checkContents = String(check.toByteArray, UTF_8)
286+
inContext(rootCtx.fresh.setCompilationUnit(freshUnit)):
287+
testSamePrinted(beforePicklingPrinted(cls), checkContents, cls, check)
288+
case None =>
289+
()
290+
271291
inContext(printerContext(testJava)(using rootCtx.fresh.setCompilationUnit(freshUnit))):
272292
testSame(i"$unpickled%\n%", beforePickling(cls), cls)
273293

@@ -283,4 +303,15 @@ class Pickler extends Phase {
283303
|
284304
| diff before-pickling.txt after-pickling.txt""")
285305
end testSame
306+
307+
private def testSamePrinted(printed: String, checkContents: String, cls: ClassSymbol, check: AbstractFile)(using Context) =
308+
import java.nio.charset.StandardCharsets.UTF_8
309+
def normal(s: String) = new String(s.getBytes(UTF_8), UTF_8)
310+
val unequal = printed.length() != checkContents.length() || normal(printed) != normal(checkContents)
311+
if unequal then
312+
output("after-printing.txt", printed)
313+
report.error(em"""TASTy printer difference for $cls in ${cls.source}, for details:
314+
|
315+
| diff ${check.toString} after-printing.txt""")
316+
end testSamePrinted
286317
}

tests/pos/i19806/J.tastycheck

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
Header:
2+
version: <elided>
3+
tooling: <elided>
4+
UUID: <elided>
5+
6+
Names (217 bytes, starting from 80):
7+
0: ASTs
8+
1: p
9+
2: J
10+
3: J[ModuleClass]
11+
4: Object
12+
5: java
13+
6: lang
14+
7: java[Qualified . lang]
15+
8: _
16+
9: <init>
17+
10: Unit
18+
11: scala
19+
12: module2
20+
13: Module
21+
14: Module[ModuleClass]
22+
15: module
23+
16: innermodule2
24+
17: InnerModule
25+
18: InnerModule[ModuleClass]
26+
19: innermodule
27+
20: T
28+
21: Nothing
29+
22: Positions
30+
23: tests/pos/i19806/J_SCALA_ONLY.java
31+
24: Comments
32+
25: Attributes
33+
34+
35+
Trees (145 bytes, starting from 300):
36+
0: PACKAGE(142)
37+
3: TERMREFpkg 1 [p]
38+
5: VALDEF(11) 2 [J]
39+
8: IDENTtpt 3 [J[ModuleClass]]
40+
10: TYPEREFsymbol 18
41+
12: TERMREFpkg 1 [p]
42+
14: ELIDED
43+
15: SHAREDtype 10
44+
17: OBJECT
45+
18: TYPEDEF(86) 3 [J[ModuleClass]]
46+
21: TEMPLATE(82)
47+
23: TYPEREF 4 [Object]
48+
25: TERMREFpkg 7 [java[Qualified . lang]]
49+
27: SELFDEF 8 [_]
50+
29: SINGLETONtpt
51+
30: TERMREFsymbol 5
52+
32: SHAREDtype 12
53+
34: DEFDEF(7) 9 [<init>]
54+
37: EMPTYCLAUSE
55+
38: TYPEREF 10 [Unit]
56+
40: TERMREFpkg 11 [scala]
57+
42: STABLE
58+
43: DEFDEF(12) 12 [module2]
59+
46: EMPTYCLAUSE
60+
47: IDENTtpt 14 [Module[ModuleClass]]
61+
49: TYPEREF 14 [Module[ModuleClass]]
62+
51: SHAREDtype 12
63+
53: ELIDED
64+
54: SHAREDtype 49
65+
56: STATIC
66+
57: DEFDEF(12) 15 [module]
67+
60: EMPTYCLAUSE
68+
61: SELECTtpt 14 [Module[ModuleClass]]
69+
63: SHAREDtype 3
70+
65: ELIDED
71+
66: TYPEREF 14 [Module[ModuleClass]]
72+
68: SHAREDtype 3
73+
70: STATIC
74+
71: DEFDEF(14) 16 [innermodule2]
75+
74: EMPTYCLAUSE
76+
75: SELECTtpt 18 [InnerModule[ModuleClass]]
77+
77: TERMREF 13 [Module]
78+
79: SHAREDtype 12
79+
81: ELIDED
80+
82: TYPEREF 18 [InnerModule[ModuleClass]]
81+
84: SHAREDtype 77
82+
86: STATIC
83+
87: DEFDEF(16) 19 [innermodule]
84+
90: EMPTYCLAUSE
85+
91: SELECTtpt 18 [InnerModule[ModuleClass]]
86+
93: SELECT 13 [Module]
87+
95: SHAREDtype 3
88+
97: ELIDED
89+
98: TYPEREF 18 [InnerModule[ModuleClass]]
90+
100: TERMREF 13 [Module]
91+
102: SHAREDtype 3
92+
104: STATIC
93+
105: OBJECT
94+
106: TYPEDEF(37) 2 [J]
95+
109: TEMPLATE(34)
96+
111: TYPEPARAM(11) 20 [T]
97+
114: TYPEBOUNDS(6)
98+
116: TYPEREF 21 [Nothing]
99+
118: SHAREDtype 40
100+
120: SHAREDtype 23
101+
122: PRIVATE
102+
123: LOCAL
103+
124: SHAREDtype 120
104+
126: SPLITCLAUSE
105+
127: DEFDEF(16) 9 [<init>]
106+
130: TYPEPARAM(7) 20 [T]
107+
133: TYPEBOUNDStpt(4)
108+
135: SHAREDtype 116
109+
137: SHAREDtype 120
110+
139: EMPTYCLAUSE
111+
140: SHAREDtype 38
112+
142: ELIDED
113+
143: SHAREDtype 38
114+
145:
115+
116+
Positions (145 bytes, starting from 448):
117+
lines: 23
118+
line sizes:
119+
10, 0, 19, 0, 15, 0, 35, 29, 3, 0, 36, 29, 3, 0, 52, 41, 3, 0, 53, 41
120+
3, 1, 0
121+
positions:
122+
0: 0 .. 394
123+
5: 12 .. 12
124+
8: 12 .. 12
125+
18: 12 .. 394
126+
21: 52 .. 392
127+
23: 25 .. 25
128+
30: 52 .. 52
129+
34: 52 .. 52
130+
38: 52 .. 52
131+
43: 52 .. 119
132+
47: 66 .. 73
133+
57: 123 .. 191
134+
61: 137 .. 146
135+
63: 137 .. 138
136+
71: 195 .. 291
137+
75: 209 .. 228
138+
77: 209 .. 215
139+
87: 295 .. 392
140+
91: 309 .. 330
141+
93: 309 .. 317
142+
95: 309 .. 310
143+
106: 12 .. 394
144+
109: 27 .. 48
145+
111: 27 .. 28
146+
114: 27 .. 27
147+
124: 28 .. 28
148+
127: 35 .. 48
149+
130: 27 .. 28
150+
135: 27 .. 27
151+
137: 27 .. 27
152+
140: 46 .. 46
153+
154+
source paths:
155+
0: 23 [tests/pos/i19806/J_SCALA_ONLY.java]
156+
157+
158+
Attributes (4 bytes, starting from 597):
159+
JAVAattr
160+
OUTLINEattr
161+
SOURCEFILEattr 23 [tests/pos/i19806/J_SCALA_ONLY.java]

tests/pos/i19806/J_SCALA_ONLY.java

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package p;
2+
3+
public class J<T> {
4+
5+
public J() {}
6+
7+
public static Module$ module2() {
8+
return p.Module$.MODULE$;
9+
}
10+
11+
public static p.Module$ module() {
12+
return p.Module$.MODULE$;
13+
}
14+
15+
public static Module.InnerModule$ innermodule2() {
16+
return p.Module.InnerModule$.MODULE$;
17+
}
18+
19+
public static p.Module.InnerModule$ innermodule() {
20+
return p.Module.InnerModule$.MODULE$;
21+
}
22+
}

tests/pos/i19806/Module.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options -Yjava-tasty -Ytest-pickler-check
2+
3+
package p
4+
5+
object Module:
6+
object InnerModule
7+
8+
class Outer:
9+
object InnerModule

tests/run/i17255/Module.scala

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// scalajs: --skip
12
package p {
23
object Module {
34
override def toString = "Module"

0 commit comments

Comments
 (0)