PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/orc/compile/OrcCompiler.scala

https://github.com/laurenyew/cOrcS
Scala | 375 lines | 268 code | 40 blank | 67 comment | 26 complexity | f3fa36203a145fc2dd227978d93b2ff4 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. //
  2. // OrcCompiler.scala -- Scala classes CoreOrcCompiler and StandradOrcCompiler
  3. // Project OrcScala
  4. //
  5. // $Id: OrcCompiler.scala 3109 2012-10-02 20:38:58Z laurenyew $
  6. //
  7. // Created by jthywiss on May 26, 2010.
  8. //
  9. // Copyright (c) 2011 The University of Texas at Austin. All rights reserved.
  10. //
  11. // Use and redistribution of this file is governed by the license terms in
  12. // the LICENSE file found in the project's top-level directory and also found at
  13. // URL: http://orc.csres.utexas.edu/license.shtml .
  14. //
  15. package orc.compile
  16. import java.io.{ BufferedReader, File, FileNotFoundException, IOException, PrintWriter, Writer, FileOutputStream }
  17. import java.net.{ MalformedURLException, URI, URISyntaxException }
  18. import orc.{ OrcCompilationOptions, OrcCompiler }
  19. import orc.compile.optimize._
  20. import orc.compile.parse.{ OrcNetInputContext, OrcResourceInputContext, OrcInputContext, OrcProgramParser, OrcIncludeParser }
  21. import orc.compile.translate.Translator
  22. import orc.compile.typecheck.Typechecker
  23. import orc.error.compiletime._
  24. import orc.error.compiletime.CompileLogger.Severity
  25. import orc.error.OrcExceptionExtension._
  26. import orc.progress.{ NullProgressMonitor, ProgressMonitor }
  27. import orc.values.sites.SiteClassLoading
  28. import scala.collection.JavaConversions._
  29. import scala.compat.Platform.currentTime
  30. import orc.ast.oil.xml.OrcXML
  31. import orc.compile.securityAnalysis.securityChecker
  32. import orc.compile.securityAnalysis.SecurityLevel
  33. /** Represents a configuration state for a compiler.
  34. */
  35. class CompilerOptions(val options: OrcCompilationOptions, val logger: CompileLogger) {
  36. def reportProblem(exn: CompilationException with ContinuableSeverity) {
  37. logger.recordMessage(exn.severity, 0, exn.getMessage(), exn.getPosition(), exn)
  38. }
  39. }
  40. /** Represents one phase in a compiler. It is defined as a function from
  41. * compiler options to a function from a phase's input to its output.
  42. * CompilerPhases can be composed with the >>> operator.
  43. *
  44. * @author jthywiss
  45. */
  46. /*
  47. * O -- type of options
  48. * A, B -- type of ast of input and output respectively
  49. */
  50. trait CompilerPhase[O, A, B] extends (O => A => B) { self =>
  51. val phaseName: String
  52. def >>>[C >: Null](that: CompilerPhase[O, B, C]) = new CompilerPhase[O, A, C] {
  53. val phaseName = self.phaseName + " >>> " + that.phaseName
  54. override def apply(o: O) = { a: A =>
  55. if (a == null) null else {
  56. val b = self.apply(o)(a)
  57. if (b == null) null else {
  58. that(o)(b)
  59. }
  60. }
  61. }
  62. }
  63. //tells how long phase ran
  64. def timePhase: CompilerPhase[O, A, B] = new CompilerPhase[O, A, B] {
  65. val phaseName = self.phaseName
  66. override def apply(o: O) = { a: A =>
  67. val phaseStart = currentTime
  68. val b = self.apply(o)(a)
  69. val phaseEnd = currentTime
  70. Logger.fine("phase duration: " + phaseName + ": " + (phaseEnd - phaseStart) + " ms")
  71. b
  72. }
  73. }
  74. def printOut: CompilerPhase[O, A, B] = new CompilerPhase[O, A, B] {
  75. val phaseName = self.phaseName
  76. override def apply(o: O) = { a: A =>
  77. val b = self.apply(o)(a)
  78. Logger.info(phaseName + " result = " + b.toString())
  79. b
  80. }
  81. }
  82. }
  83. /** An instance of CoreOrcCompiler is a particular Orc compiler configuration,
  84. * which is a particular Orc compiler implementation, in a JVM instance.
  85. * Note, however, that an CoreOrcCompiler instance is not specialized for
  86. * a single Orc program; in fact, multiple compilations of different programs,
  87. * with different options set, may be in progress concurrently within a
  88. * single CoreOrcCompiler instance.
  89. *
  90. * @author jthywiss
  91. */
  92. abstract class CoreOrcCompiler extends OrcCompiler {
  93. ////////
  94. // Definition of the phases of the compiler
  95. ////////
  96. //parser phase
  97. val parse = new CompilerPhase[CompilerOptions, OrcInputContext, orc.ast.ext.Expression] {
  98. val phaseName = "parse"
  99. @throws(classOf[IOException])
  100. override def apply(co: CompilerOptions) = { source =>
  101. val options = co.options
  102. val topLevelSourcePos = source.reader.pos
  103. var includeFileNames = options.additionalIncludes
  104. if (options.usePrelude) {
  105. includeFileNames = "prelude.inc" :: (includeFileNames).toList
  106. }
  107. val includeAsts = for (fileName <- includeFileNames) yield {
  108. val ic = openInclude(fileName, null, options)
  109. OrcIncludeParser(ic, options, CoreOrcCompiler.this) match {
  110. case r: OrcIncludeParser.SuccessT[_] => r.get.asInstanceOf[OrcIncludeParser.ResultType]
  111. case n: OrcIncludeParser.NoSuccess => throw new ParsingException(n.msg, n.next.pos)
  112. }
  113. }
  114. val progAst = OrcProgramParser(source, options, CoreOrcCompiler.this) match {
  115. case r: OrcProgramParser.SuccessT[_] => r.get.asInstanceOf[OrcProgramParser.ResultType]
  116. case n: OrcProgramParser.NoSuccess => throw new ParsingException(n.msg, n.next.pos)
  117. }
  118. (includeAsts :\ progAst) { orc.ast.ext.Declare } setPos topLevelSourcePos
  119. }
  120. }
  121. val translate = new CompilerPhase[CompilerOptions, orc.ast.ext.Expression, orc.ast.oil.named.Expression] {
  122. val phaseName = "translate"
  123. override def apply(co: CompilerOptions) =
  124. { ast =>
  125. val translator = new Translator(co reportProblem _)
  126. translator.translate(ast)
  127. }
  128. }
  129. val noUnboundVars = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  130. val phaseName = "noUnboundVars"
  131. override def apply(co: CompilerOptions) = { ast =>
  132. for (x <- ast.unboundvars) {
  133. co.reportProblem(UnboundVariableException(x.name) at x)
  134. }
  135. for (u <- ast.unboundtypevars) {
  136. co.reportProblem(UnboundTypeVariableException(u.name) at u)
  137. }
  138. ast
  139. }
  140. }
  141. val fractionDefs = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  142. val phaseName = "fractionDefs"
  143. override def apply(co: CompilerOptions) = { FractionDefs(_) }
  144. }
  145. val removeUnusedDefs = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  146. val phaseName = "removeUnusedDefs"
  147. override def apply(co: CompilerOptions) = { ast => RemoveUnusedDefs(ast) }
  148. }
  149. val removeUnusedTypes = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  150. val phaseName = "removeUnusedTypes"
  151. override def apply(co: CompilerOptions) = { ast => RemoveUnusedTypes(ast) }
  152. }
  153. val typeCheck = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  154. val phaseName = "typeCheck"
  155. override def apply(co: CompilerOptions) = { ast =>
  156. if (co.options.typecheck) {
  157. val (newAst, programType) = Typechecker(ast)
  158. val typeReport = "Program type checks as " + programType.toString
  159. co.logger.recordMessage(CompileLogger.Severity.INFO, 0, typeReport, newAst.pos, newAst)
  160. newAst
  161. } else {
  162. ast
  163. }
  164. }
  165. }
  166. /**
  167. * SecurityLevel Analysis Phase
  168. */
  169. val securityLevelCheck = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  170. val phaseName = "securityLevelCheck"
  171. override def apply(co: CompilerOptions) = { ast =>
  172. if(co.options.securityCheck){
  173. //initialize the lattice
  174. SecurityLevel.initializeGraph()
  175. val (newAst, programSL) = securityChecker(ast)//ast is named.Expresion
  176. var slReport = ""
  177. if(programSL != null)
  178. slReport = "Program output security level checks as " + programSL.toString
  179. else
  180. slReport = "There are no security levels in the output"
  181. Console.println(slReport)
  182. //compiler records messages in a list, caller supplies implemenation to display to user
  183. /**co.logger.recordMessage(CompileLogger.Severity.INFO, 0, slReport, newAst.pos, newAst)
  184. */
  185. }
  186. ast//return original ast
  187. }
  188. }
  189. val noUnguardedRecursion = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.named.Expression] {
  190. val phaseName = "noUnguardedRecursion"
  191. override def apply(co: CompilerOptions) =
  192. { ast =>
  193. def warn(e: orc.ast.oil.named.Expression) = {
  194. co.reportProblem(UnguardedRecursionException() at e)
  195. }
  196. if (!co.options.disableRecursionCheck) {
  197. ast.checkGuarded(warn)
  198. }
  199. ast
  200. }
  201. }
  202. val deBruijn = new CompilerPhase[CompilerOptions, orc.ast.oil.named.Expression, orc.ast.oil.nameless.Expression] {
  203. val phaseName = "deBruijn"
  204. override def apply(co: CompilerOptions) = { ast => ast.withoutNames }
  205. }
  206. // Generate XML for the AST and echo it to console; useful for testing.
  207. val outputOil = new CompilerPhase[CompilerOptions, orc.ast.oil.nameless.Expression, orc.ast.oil.nameless.Expression] {
  208. val phaseName = "outputOil"
  209. override def apply(co: CompilerOptions) = { ast =>
  210. if (co.options.echoOil) {
  211. val xml = OrcXML.astToXml(ast)
  212. val xmlnice = {
  213. val pp = new scala.xml.PrettyPrinter(80, 2)
  214. val xmlheader = """<?xml version="1.0" encoding="UTF-8" ?>""" + "\n"
  215. xmlheader + pp.format(xml)
  216. }
  217. println("Echoing OIL to console.")
  218. println("Caution: Echo on console will not accurately reproduce whitespace in string constants.")
  219. println()
  220. println(xmlnice)
  221. }
  222. co.options.oilOutputFile match {
  223. case Some(f) => {
  224. OrcXML.writeOilToStream(ast, new FileOutputStream(f))
  225. }
  226. case None => {}
  227. }
  228. ast
  229. }
  230. }
  231. ////////
  232. // Compose phases into a compiler
  233. ////////
  234. val phases =
  235. parse.timePhase >>>
  236. translate.timePhase >>>
  237. noUnboundVars.timePhase >>>
  238. fractionDefs.timePhase >>>
  239. typeCheck.timePhase >>>
  240. securityLevelCheck.timePhase >>> //timePhase keeps statistics
  241. removeUnusedDefs.timePhase >>>
  242. removeUnusedTypes.timePhase >>>
  243. noUnguardedRecursion.timePhase >>>
  244. deBruijn.timePhase >>>
  245. outputOil
  246. ////////
  247. // Compiler methods
  248. ////////
  249. @throws(classOf[IOException])
  250. def apply(source: OrcInputContext, options: OrcCompilationOptions, compileLogger: CompileLogger, progress: ProgressMonitor): orc.ast.oil.nameless.Expression = {
  251. //Logger.config(options)
  252. Logger.config("Begin compile " + options.filename)
  253. compileLogger.beginProcessing(options.filename)
  254. try {
  255. val result = phases(new CompilerOptions(options, compileLogger))(source)
  256. if (compileLogger.getMaxSeverity().ordinal() >= Severity.ERROR.ordinal()) null else result
  257. } catch {
  258. case e: CompilationException =>
  259. compileLogger.recordMessage(Severity.FATAL, 0, e.getMessage, e.getPosition(), null, e)
  260. null
  261. } finally {
  262. compileLogger.endProcessing(options.filename)
  263. Logger.config("End compile " + options.filename)
  264. }
  265. }
  266. }
  267. /** StandardOrcCompiler extends CoreOrcCompiler with "standard" environment interfaces.
  268. *
  269. * @author jthywiss
  270. */
  271. class StandardOrcCompiler() extends CoreOrcCompiler with SiteClassLoading {
  272. @throws(classOf[IOException])
  273. override def apply(source: OrcInputContext, options: OrcCompilationOptions, compileLogger: CompileLogger, progress: ProgressMonitor): orc.ast.oil.nameless.Expression = {
  274. SiteClassLoading.initWithClassPathStrings(options.classPath)
  275. super.apply(source, options, compileLogger, progress)
  276. }
  277. private class OrcReaderInputContext(val javaReader: java.io.Reader, override val descr: String) extends OrcInputContext {
  278. val file = new File(descr)
  279. override val reader = orc.compile.parse.OrcReader(new BufferedReader(javaReader), descr)
  280. override def toURI = file.toURI
  281. override def toURL = toURI.toURL
  282. }
  283. @throws(classOf[IOException])
  284. def apply(source: java.io.Reader, options: OrcCompilationOptions, err: Writer): orc.ast.oil.nameless.Expression = {
  285. this(new OrcReaderInputContext(source, options.filename), options, new PrintWriterCompileLogger(new PrintWriter(err, true)), NullProgressMonitor)
  286. }
  287. private object OrcNullInputContext extends OrcInputContext {
  288. override val descr = ""
  289. override val reader = null
  290. override val toURI = new URI("")
  291. override def toURL = throw new UnsupportedOperationException("OrcNullInputContext.toURL")
  292. }
  293. @throws(classOf[IOException])
  294. def openInclude(includeFileName: String, relativeTo: OrcInputContext, options: OrcCompilationOptions): OrcInputContext = {
  295. val baseIC = if (relativeTo != null) relativeTo else OrcNullInputContext
  296. Logger.finer("openInclude " + includeFileName + ", relative to " + Option(baseIC.getClass.getCanonicalName).getOrElse(baseIC.getClass.getName) + "(" + baseIC.descr + ")")
  297. // If no include path, allow absolute HTTP and HTTPS includes
  298. if (options.includePath.isEmpty && (includeFileName.toLowerCase.startsWith("http://") || includeFileName.toLowerCase.startsWith("https://"))) {
  299. try {
  300. val newIC = new OrcNetInputContext(new URI(includeFileName))
  301. Logger.finer("include " + includeFileName + " opened as " + Option(newIC.getClass.getCanonicalName).getOrElse(newIC.getClass.getName) + "(" + newIC.descr + ")")
  302. return newIC
  303. } catch {
  304. case e: URISyntaxException => throw new FileNotFoundException("Include file '" + includeFileName + "' not found; Check URI syntax (" + e.getMessage + ")");
  305. case e: MalformedURLException => throw new FileNotFoundException("Include file '" + includeFileName + "' not found; Check URI syntax (" + e.getMessage + ")");
  306. case e: IOException => throw new FileNotFoundException("Include file '" + includeFileName + "' not found; IO error (" + e.getMessage + ")");
  307. }
  308. }
  309. // Try filename under the include path list
  310. for (incPath <- scala.collection.JavaConversions.collectionAsScalaIterable(options.includePath)) {
  311. try {
  312. //FIXME: Security implications of including local files:
  313. // For Orchard's sake, OrcJava disallowed relative file names
  314. // in certain cases, to prevent examining files by including
  315. // them. This seems a weak barrier, and in fact was broken.
  316. // We need an alternative way to control local file reads.
  317. val newIC = baseIC.newInputFromPath(incPath, includeFileName)
  318. Logger.finer("include " + includeFileName + ", found on include path entry " + incPath + ", opened as " + Option(newIC.getClass.getCanonicalName).getOrElse(newIC.getClass.getName) + "(" + newIC.descr + ")")
  319. return newIC
  320. } catch {
  321. case _: IOException => /* Ignore, must not be here */
  322. }
  323. }
  324. // Try in the bundled include resources
  325. try {
  326. val newIC = new OrcResourceInputContext("orc/lib/includes/" + includeFileName, getResource)
  327. Logger.finer("include " + includeFileName + ", found in bundled resources, opened as " + Option(newIC.getClass.getCanonicalName).getOrElse(newIC.getClass.getName) + "(" + newIC.descr + ")")
  328. return newIC
  329. } catch {
  330. case _: IOException => /* Ignore, must not be here */
  331. }
  332. Logger.finer("include " + includeFileName + " not found")
  333. throw new FileNotFoundException("Include file '" + includeFileName + "' not found; check the include path.");
  334. }
  335. }