-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Create DecompilerPrinter and ReplPrinter #3691
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
Conversation
Not sure it is the correct solution. It prints invalid trees after lambda lift: class Test {
def test = {
val y = 2
(x: Int) => x + y
}
} package <empty> {
@scala.annotation.internal.SourceFile("tests/allan/Test.scala") class Test
extends
Object {
def <init>(): Unit =
{
super()
()
}
def test(): Function1 =
{
val y: Int = 2
{
}
}
(y$1: Int, x: Int) =>
{
x.+(y$1)
}
}
} scalac: def test(): Function1 = {
val y: Int = 2;
{
((x: Int) => Test.this.$anonfun$test$1(y, x))
}
};
final <artifact> private[this] def $anonfun$test$1(y$1: Int, x: Int): Int = x.+(y$1) |
This option is not supposed to be used after lambda lift. In fact it is only intended for pickling level of tree abstractions. I could make it a Y setting. |
Can't we have a solution that works across all phases? The way we currently print closures doesn't feel natural to me: def test(): Function1 =
{
val y: Int = 2
{
closure(y | <empty>.this.test$$anonfun$1:JFunction1$mcII$sp)
}
}
private <static> def test$$anonfun$1(y$1: Int, x: Int): Int =
{
x.+(y$1)
} It would be great to print them like scalac does (at least under an option). |
It should print the normal output if we are in a transformation phase. No normal user should ever see the trees in those. |
Don't you don't think this: def test(): Function1 =
{
val y: Int = 2
{
closure(y | <empty>.this.test$$anonfun$1:JFunction1$mcII$sp)
}
} would be better printed as: def test(): Function1 =
{
val y: Int = 2
{
(x: Int) => <empty>.this.test$$anonfun$1(y: Int, x: Int)
}
} It is still the same tree, just a different way to print it |
@allanrenucci changing printers in transforms is outside of the scope of this PR |
If the scope of this PR is only the decompiler then I don't think it is worth the added complexity to |
There would be a lot of duplication if we do not put it in the |
209ae55
to
2166012
Compare
Note that we also have a https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/printing/UserFacingPrinter.scala (which I'd like to get rid of if possible), so calling this option -Yprint-user is confusing. |
We should probably move that logic from What about |
d34b0dc
to
3d9e71d
Compare
@smarter all branches of the code in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've a general concern to make RefinedPrinter
use-scenario-aware: repl
, decompile
are both specific usage of RefinedPrinter.
I think it's better to add functional options (usage-neutral), like printPos
, printLines
, printTypes
, etc. In current PR, maybe something like printFriendly
to print friendly syntax for While/DoWhile
and function literals?
For the case of Repl
, I think it's better to create a separate Printer for it, as it's really usage-specific.
@@ -14,7 +14,8 @@ import TypeApplications._ | |||
import Decorators._ | |||
import config.Config | |||
import util.Positions._ | |||
import transform.SymUtils._ | |||
import dotty.tools.dotc.transform.SymUtils._ | |||
import dotty.tools.dotc.transform.FirstTransform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the prefix dotty.tools.dotc
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, confused the IDE
@@ -27,6 +28,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { | |||
private[this] var myCtx: Context = _ctx | |||
private[this] var printPos = ctx.settings.YprintPos.value | |||
private[this] val printLines = ctx.settings.printLines.value | |||
private[this] val YprintDecompile = | |||
ctx.settings.YprintDecompile.value && !ctx.phases.dropWhile(!_.isInstanceOf[FirstTransform]).contains(ctx.phase) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment to explain the latter condition?
I tend to agree with @liufengyun here. Should we discuss this PR during the next Dotty meeting? |
f77825b
to
65cb973
Compare
65cb973
to
6f81983
Compare
First 3 commits from #3701 |
@liufengyun the last commit is ready for review. The first 3 are based on another PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
BTW, other things that can be handled as well (possibly in a different PR):
- While/DoWhile encoding
- String Interpolator encoding
- Anonymous class encoding
@@ -14,23 +18,41 @@ class DecompilationPrinter extends Phase { | |||
override def phaseName: String = "decompilationPrinter" | |||
|
|||
override def run(implicit ctx: Context): Unit = { | |||
val unit = ctx.compilationUnit | |||
val outputDir = ctx.settings.outputDir.value | |||
if (outputDir == ".") printToOutput(System.out) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So here we assume print to current folder means printing to standard console?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as it is the default it is practical.
var os: OutputStream = null | ||
var ps: PrintStream = null | ||
try { | ||
os = File(outputDir + ".decompiled").outputStream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dir + .decompiled
: filename missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I use the outputDir
as a file. Just like we do with JARs, where we write -d Foo.jar
. We can change the format later if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mode is mostly intended for testing
|
||
class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { | ||
|
||
override protected def modText(mods: untpd.Modifiers, kw: String): Text = { // DD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to have a comment to document what's changed/overriden. The same for other overwritten methods.
Or, can we share more code with base class to reduce the overriding changes? The same for other overwritten methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will go for the second. Documenting all changes might end in the large and unmaintainable list of features when we fix more stuff. I would rely more on the commit history and the tests added for that change.
@@ -64,7 +78,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { | |||
protected val PrintableFlags = (SourceModifierFlags | Label | Module | Local).toCommonFlags | |||
|
|||
override def nameString(name: Name): String = | |||
if (ctx.settings.YdebugNames.value) name.debugString else name.toString | |||
if (name.isReplAssignName) name.decode.toString.takeWhile(_ != '$') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Override nameString
in ReplPrinter
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, missed it.
Enable flag by default for the decompiler