summaryrefslogtreecommitdiff
path: root/examples/scala-js/cli/src/main/scala/scala/scalajs/cli/Scalajsp.scala
blob: 0d64b9398778a0671931ca3670d2c358a347938d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js CLI               **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013-2014, LAMP/EPFL   **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-js.org/       **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */


package scala.scalajs.cli

import scala.scalajs.ir
import ir.ScalaJSVersions
import ir.Trees.{Tree, ClassDef}
import ir.Printers.{InfoPrinter, IRTreePrinter}

import scala.scalajs.tools.sem.Semantics
import scala.scalajs.tools.javascript
import javascript.ScalaJSClassEmitter
import javascript.Printers.JSTreePrinter

import scala.scalajs.tools.io._
import scala.collection.immutable.Seq

import java.io.{Console => _, _}
import java.util.zip.{ZipFile, ZipEntry}

object Scalajsp {

  case class Options(
    infos: Boolean = false,
    desugar: Boolean = false,
    showReflProxy: Boolean = false,
    jar: Option[File] = None,
    fileNames: Seq[String] = Seq.empty)

  def main(args: Array[String]): Unit = {
    val parser = new scopt.OptionParser[Options]("scalajsp") {
      head("scalajsp", ScalaJSVersions.current)
      arg[String]("<file> ...")
        .unbounded()
        .action { (x, c) => c.copy(fileNames = c.fileNames :+ x) }
        .text("*.sjsir file to display content of")
      opt[File]('j', "jar")
        .valueName("<jar>")
        .action { (x, c) => c.copy(jar = Some(x)) }
        .text("Read *.sjsir file(s) from the given JAR.")
      opt[Unit]('d', "desugar")
        .action { (_, c) => c.copy(desugar = true) }
        .text("Desugar JS trees. This yields runnable JavaScript")
      opt[Unit]('i', "infos")
        .action { (_, c) => c.copy(infos = true) }
        .text("Show DCE infos instead of trees")
      opt[Unit]('p', "reflProxies")
        .action { (_, c) => c.copy(showReflProxy = true) }
        .text("Show reflective call proxies")
      opt[Unit]('s', "supported")
        .action { (_,_) => printSupported(); sys.exit() }
        .text("Show supported Scala.js IR versions")
      version("version")
        .abbr("v")
        .text("Show scalajsp version")
      help("help")
        .abbr("h")
        .text("prints this usage text")

      override def showUsageOnError = true
    }

    for {
      options  <- parser.parse(args, Options())
      fileName <- options.fileNames
    } {
      val vfile = options.jar map { jar =>
        readFromJar(jar, fileName)
      } getOrElse {
        readFromFile(fileName)
      }

      displayFileContent(vfile, options)
    }
  }

  def printSupported(): Unit = {
    import ScalaJSVersions._
    println(s"Emitted Scala.js IR version is: $binaryEmitted")
    println("Supported Scala.js IR versions are")
    binarySupported.foreach(v => println(s"* $v"))
  }

  def displayFileContent(vfile: VirtualScalaJSIRFile, opts: Options): Unit = {
    if (opts.infos)
      new InfoPrinter(stdout).printClassInfo(vfile.info)
    else {
      val outTree = {
        if (opts.showReflProxy) vfile.tree
        else filterOutReflProxies(vfile.tree)
      }

      if (opts.desugar)
        new JSTreePrinter(stdout).printTopLevelTree(
            new ScalaJSClassEmitter(Semantics.Defaults).genClassDef(outTree))
      else
        new IRTreePrinter(stdout).printTopLevelTree(outTree)
    }

    stdout.flush()
  }

  private def fail(msg: String) = {
    Console.err.println(msg)
    sys.exit(1)
  }

  private def readFromFile(fileName: String) = {
    val file = new File(fileName)

    if (!file.exists)
      fail(s"No such file: $fileName")
    else if (!file.canRead)
      fail(s"Unable to read file: $fileName")
    else
      FileVirtualScalaJSIRFile(file)
  }

  private def readFromJar(jar: File, name: String) = {
    val jarFile =
      try { new ZipFile(jar) }
      catch { case _: FileNotFoundException => fail(s"No such JAR: $jar") }
    try {
      val entry = jarFile.getEntry(name)
      if (entry == null)
        fail(s"No such file in jar: $name")
      else {
        val name = jarFile.getName + "#" + entry.getName
        val content =
          IO.readInputStreamToByteArray(jarFile.getInputStream(entry))
        new MemVirtualSerializedScalaJSIRFile(name).withContent(content)
      }
    } finally {
      jarFile.close()
    }
  }

  private val stdout =
    new BufferedWriter(new OutputStreamWriter(Console.out, "UTF-8"))

  private def filterOutReflProxies(tree: ClassDef): ClassDef = {
    import ir.Trees._
    import ir.Definitions.isReflProxyName
    val newDefs = tree.defs.filter {
      case MethodDef(Ident(name, _), _, _, _) => !isReflProxyName(name)
      case _ => true
    }
    tree.copy(defs = newDefs)(tree.pos)
  }

}