Skip to content

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

Merged
merged 6 commits into from
Jan 18, 2018

Conversation

nicolasstucki
Copy link
Contributor

Enable flag by default for the decompiler

@nicolasstucki
Copy link
Contributor Author

From this
screen shot 2017-12-22 at 09 41 55
to this
screen shot 2017-12-22 at 09 41 39 1

@allanrenucci
Copy link
Contributor

allanrenucci commented Dec 22, 2017

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)

@nicolasstucki
Copy link
Contributor Author

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.

@allanrenucci
Copy link
Contributor

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).

@nicolasstucki
Copy link
Contributor Author

It should print the normal output if we are in a transformation phase. No normal user should ever see the trees in those.

@allanrenucci
Copy link
Contributor

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

@nicolasstucki nicolasstucki changed the title Add -XprintUser and pretty print lambdas with it Add -XprintUser to pretty print for the decompiler Dec 22, 2017
@nicolasstucki
Copy link
Contributor Author

@allanrenucci changing printers in transforms is outside of the scope of this PR

@allanrenucci
Copy link
Contributor

allanrenucci commented Dec 22, 2017

If the scope of this PR is only the decompiler then I don't think it is worth the added complexity to RefinedPrinter. I would rather create a special printer for the decompiler (a subclass of RefinedPrinter) and not introduce a new setting.

@nicolasstucki
Copy link
Contributor Author

There would be a lot of duplication if we do not put it in the RefinedPrinter. And we would have to maintain any change in two parallel printers.

@smarter
Copy link
Member

smarter commented Dec 24, 2017

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.

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Dec 24, 2017

We should probably move that logic from UserFacingPrinter to PlainPrinter as well.

What about -Yprint-decompile?

@nicolasstucki nicolasstucki force-pushed the print-lambdas branch 2 times, most recently from d34b0dc to 3d9e71d Compare December 28, 2017 17:00
@nicolasstucki
Copy link
Contributor Author

@smarter all branches of the code in UserFacingPrinter were covered by tests.

@nicolasstucki nicolasstucki requested a review from smarter January 2, 2018 11:03
@nicolasstucki nicolasstucki changed the title Add -XprintUser to pretty print for the decompiler Add -YprintDecompile and -YprintRepl to pretty print for the decompiler and repl Jan 5, 2018
@nicolasstucki nicolasstucki requested review from liufengyun and removed request for smarter January 10, 2018 12:21
@nicolasstucki nicolasstucki assigned liufengyun and unassigned smarter Jan 10, 2018
Copy link
Contributor

@liufengyun liufengyun left a 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
Copy link
Contributor

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 ?

Copy link
Contributor Author

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)
Copy link
Contributor

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?

@allanrenucci
Copy link
Contributor

I've a general concern to make RefinedPrinter use-scenario-aware: repl, decompile are both specific usage of RefinedPrinter.

I tend to agree with @liufengyun here. Should we discuss this PR during the next Dotty meeting?

@nicolasstucki nicolasstucki force-pushed the print-lambdas branch 5 times, most recently from f77825b to 65cb973 Compare January 17, 2018 08:09
@nicolasstucki nicolasstucki changed the title Add -YprintDecompile and -YprintRepl to pretty print for the decompiler and repl Create DecompilerPrinter and ReplPrinter Jan 17, 2018
@nicolasstucki
Copy link
Contributor Author

First 3 commits from #3701

@nicolasstucki
Copy link
Contributor Author

@liufengyun the last commit is ready for review. The first 3 are based on another PR.

Copy link
Contributor

@liufengyun liufengyun left a 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)
Copy link
Contributor

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?

Copy link
Contributor Author

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()
Copy link
Contributor

Choose a reason for hiding this comment

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

dir + .decompiled: filename missing?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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(_ != '$')
Copy link
Contributor

Choose a reason for hiding this comment

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

Override nameString in ReplPrinter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, missed it.

@liufengyun liufengyun merged commit 4b75d3c into scala:master Jan 18, 2018
@liufengyun liufengyun deleted the print-lambdas branch January 18, 2018 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants