From 74caae85b18fdd1ca959041da7abd925a2e10644 Mon Sep 17 00:00:00 2001 From: Jon Keam Date: Tue, 26 Dec 2017 02:04:41 -0500 Subject: [PATCH] use installed java instead of webservice --- README.md | 8 +- app/controllers/Application.scala | 31 ++---- app/models/Code.scala | 3 - app/models/Disassembler.scala | 161 +++++++++++++++++++++++++++ app/models/DisassemblerService.scala | 39 ------- app/models/Logger.scala | 16 +++ app/views/index.scala.html | 13 +-- conf/application.conf | 14 --- public/javascripts/main.js | 11 +- 9 files changed, 195 insertions(+), 101 deletions(-) delete mode 100644 app/models/Code.scala create mode 100644 app/models/Disassembler.scala delete mode 100644 app/models/DisassemblerService.scala create mode 100644 app/models/Logger.scala diff --git a/README.md b/README.md index 73b8111..4cfd55e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Welcome to Javabytes, a disassembler for your java code. Inspired by a project b Enter some Java and this application will show you the JVM bytecode that is generated. -Currently only supports [OpenJDK9](http://openjdk.java.net/projects/jdk9/), [OpenJDK8](http://openjdk.java.net/projects/jdk8/), [OpenJDK7](http://openjdk.java.net/projects/jdk7/), and [OpenJDK6](http://openjdk.java.net/projects/jdk6/). +Currently only supports [OpenJDK8](http://openjdk.java.net/projects/jdk8/). The demo site can be found here at [javabytes.io](http://javabytes.io/). @@ -14,10 +14,14 @@ The demo site can be found here at [javabytes.io](http://javabytes.io/). ### Prereq 1. Java 8 -2. Play 2.3.1 or later +2. Scala 1.10 +3. Play 2.3.1 +4. SBT 0.13 ``` brew install typesafe-activator + # also create tmp directory + mkdir -p /tmp/javabytes ``` ### Starting the Server diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala index 90889cb..dfc3403 100644 --- a/app/controllers/Application.scala +++ b/app/controllers/Application.scala @@ -1,36 +1,21 @@ package controllers import play.api.mvc._ -import play.api.libs.json._ -import play.api.libs.functional.syntax._ -import play.api.Logger import models._ object Application extends Controller { - implicit val codeReads: Reads[Code] = ( - (JsPath \ "code").read[String] and - (JsPath \ "version").read[String] - )(Code.apply _) - def index = Action { Ok(views.html.index("")) } - def submitCode = Action(BodyParsers.parse.json) { request => - Logger.debug("submit code") - - val codeResult = request.body.validate[Code] - codeResult.fold( - errors => { - BadRequest(Json.obj("status" ->"ERROR", "message" -> JsError.toFlatJson(errors))) - }, - code => { - val helper = new DisassemblerService() - val diss = helper.disassemble(code.version, code.code) - Ok(diss) - } - ) + def submitCode = Action(parse.json) { request => + (request.body \ "code").asOpt[String].map { code => + val helper = new Disassembler() + val diss = helper.disassemble(code) + Ok(diss) + }.getOrElse { + BadRequest("Missing parameter [code]") + } } - } diff --git a/app/models/Code.scala b/app/models/Code.scala deleted file mode 100644 index 7a78dad..0000000 --- a/app/models/Code.scala +++ /dev/null @@ -1,3 +0,0 @@ -package models - -case class Code(code: String, version: String) diff --git a/app/models/Disassembler.scala b/app/models/Disassembler.scala new file mode 100644 index 0000000..67a9ddf --- /dev/null +++ b/app/models/Disassembler.scala @@ -0,0 +1,161 @@ +package models +import java.util.UUID +import java.io._ +import java.nio.file._ +import java.util.regex._ +import scala.sys.process._ +import scalax.io._ + +class Disassembler { + val tmpDir = "/tmp/javabytes/" + + def disassemble(code:String):String = { + val logger = new Logger() + var error = false + var message = "" + + //generate UUID + val rawId = "t" + UUID.randomUUID().toString() + val id = rawId.replaceAll("-", "").replaceAll("/", "").replaceAll("\\\\", "").replaceAll(":", "") + + //create tmpDir + val userTmpDirName = tmpDir + id + val userTmpDir = createDir(userTmpDirName) + + //try and extract name of class + val className = extractClassname(code) + if (className == "" || className == null) { + message += clean(logger.err, userTmpDirName) + error = true + removeDir(userTmpDir) + return message + } + + //create tmp file + val javaFilename = userTmpDirName + "/" + className + ".java" + val classFilename = userTmpDirName + "/" + className + ".class" + val fw = new FileWriter(javaFilename) + + //write code out to that file + try { + fw.write(code) + fw.flush() + } + catch { + case e: Exception => + message += "Unable to create source.
" + error = true + } + finally { + fw.close() + } + if (error) return message + + //run compiler + try { + val compilationCommand = "javac " + javaFilename + compilationCommand !! logger.log + } + catch { + case e: Exception => + message += clean(logger.err, userTmpDirName) + error = true + quietRemoveFile(javaFilename, classFilename, userTmpDir) + return message + } + + //dissassemble + var decomp = "An unknown error has occurred" + try { + val decompCommand = "javap -c " + classFilename + decomp = decompCommand !! logger.log + decomp = clean(decomp, userTmpDirName) + } + catch { + case e: Exception => + message += clean(logger.err, userTmpDirName) + error = true + quietRemoveFile(javaFilename, classFilename, userTmpDir) + return message + } + + //remove all junk files created + try { + removeFile(javaFilename, classFilename, userTmpDir) + } + catch { + case e: Exception => + message += "Unable to cleanup.
" + error = true + return message + } + + if (error) return message + else return decomp + } + + def createDir(str:String):Path = { + val folderPath = Paths.get(str) + Files.createDirectories(folderPath) + } + + def removeDir(userTmpDir:Path) = { + Files.delete(userTmpDir) + } + + def clean(str:StringBuilder, dirPrefix:String):String = { + clean(str.toString(), dirPrefix) + } + + def clean(str:String, dirPrefix:String):String = { + var decomp = str.replaceAll("\\{", "{{") + decomp = decomp.replaceAll(dirPrefix+ "/", "") + decomp = decomp.replaceAll("\\}", "}}") + decomp = decomp.replaceAll("\"", "'") + decomp = decomp.replaceAll("\n", "
") + decomp = decomp.replaceAll("\t", " ") + decomp = decomp.replaceAll("\u005E", " ") + decomp = decomp.replaceAll("\u2038", " ") + decomp = decomp.replaceAll("\\^", " ") + decomp = decomp.replaceAll("\\s\\s\\s\\s\\s+", "
") + decomp = decomp.replaceAll("\u001A", "") + decomp = decomp.replaceAll("^\\s+", "") + decomp + } + + def quietRemoveFile(filename:String, classname:String, userTmpDir:Path) = { + try { + val sourceFile = new File(filename) + sourceFile.delete() + + val classFile = new File(classname) + classFile.delete() + + Files.delete(userTmpDir) + } + catch { + case e: Exception => System.out.println("Couldn't remove file") + } + } + + def removeFile(filename:String, classname:String, userTmpDir:Path) = { + val sourceFile = new File(filename) + sourceFile.delete() + + val classFile = new File(classname) + classFile.delete() + + Files.delete(userTmpDir) + } + + def extractClassname(code:String):String = { + val pattern = Pattern.compile("\\s*(public|private)\\s+class\\s+(\\w+)\\s+((extends\\s+\\w+)|(implements\\s+\\w+( ,\\w+)*))?\\s*\\{"); + val m = pattern.matcher(code) + + if (m.find()) { + m.group(2) + } else { + null + } + } +} diff --git a/app/models/DisassemblerService.scala b/app/models/DisassemblerService.scala deleted file mode 100644 index a2bdf26..0000000 --- a/app/models/DisassemblerService.scala +++ /dev/null @@ -1,39 +0,0 @@ -package models -import scalaj.http._ -import play.api.libs.json._ -import play.api.Logger - -class DisassemblerService { - def disassemble(version:String, code:String):String = { - var decomp = "" - var message = "" - var error = false - - try { - val url = play.Play.application.configuration.getString(s"dis.api.${version}") - Logger.debug(s"url: ${url}") - val response = Json.parse(Http(url).postForm(Seq("code" -> code)).asString.body) - Logger.debug(s"response: ${response}") - - (response \ "result").asOpt[String] match { - case Some(data) => decomp = data - case _ => ; - } - - (response \ "errors").asOpt[String] match { - case Some(data) => decomp = data - case _ => ; - } - } - catch { - case e: Exception => - Logger.error(e.getMessage()) - message += "Unable to create source.
" - error = true - } - - if (error) return message - else return decomp - } - -} diff --git a/app/models/Logger.scala b/app/models/Logger.scala new file mode 100644 index 0000000..74f6c0d --- /dev/null +++ b/app/models/Logger.scala @@ -0,0 +1,16 @@ +package models +import sys.process._ + +class Logger { + val out = new StringBuilder + val err = new StringBuilder + + val log = ProcessLogger( + (o: String) => out.append(o), + (e: String) => err.append(e) + ) + + def getLog(): ProcessLogger = { + return log + } +} diff --git a/app/views/index.scala.html b/app/views/index.scala.html index e011f28..17bd785 100644 --- a/app/views/index.scala.html +++ b/app/views/index.scala.html @@ -15,19 +15,8 @@

Javabytes

-
-
- - -
-
- +