From 12d895587444d5d59e7d75dfaf7b85deb61e99e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 4 Mar 2016 12:23:10 +0100 Subject: Better encapsulation No more leaking ofMove PatchedFiles in a settings option. Move all patch classes into a `Rewrites` object. --- src/dotty/tools/dotc/Driver.scala | 1 - src/dotty/tools/dotc/Run.scala | 4 +- src/dotty/tools/dotc/config/ScalaSettings.scala | 4 +- src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- src/dotty/tools/dotc/rewrite/Patches.scala | 87 ---------------------- src/dotty/tools/dotc/rewrite/Rewrites.scala | 96 +++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 93 deletions(-) delete mode 100644 src/dotty/tools/dotc/rewrite/Patches.scala create mode 100644 src/dotty/tools/dotc/rewrite/Rewrites.scala (limited to 'src') diff --git a/src/dotty/tools/dotc/Driver.scala b/src/dotty/tools/dotc/Driver.scala index a989ad218..887274fa8 100644 --- a/src/dotty/tools/dotc/Driver.scala +++ b/src/dotty/tools/dotc/Driver.scala @@ -5,7 +5,6 @@ import config.CompilerCommand import core.Contexts.{Context, ContextBase} import util.DotClass import reporting._ -import rewrite.Patches import scala.util.control.NonFatal /** Run the Dotty compiler. diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index aba7a002d..ee808323a 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -8,7 +8,7 @@ import io.PlainFile import util.{SourceFile, NoSource, Stats, SimpleMap} import reporting.Reporter import transform.TreeChecker -import rewrite.Patches +import rewrite.Rewrites import java.io.{BufferedWriter, OutputStreamWriter} import scala.reflect.io.VirtualFile import scala.util.control.NonFatal @@ -65,7 +65,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { foreachUnit(printTree) ctx.informTime(s"$phase ", start) } - if (!ctx.reporter.hasErrors) Patches.writeBack() + if (!ctx.reporter.hasErrors) Rewrites.writeBack() } private def printTree(ctx: Context) = { diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index cdbf6ec50..a10165be2 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc package config import PathResolver.Defaults -import rewrite.Patches +import rewrite.Rewrites class ScalaSettings extends Settings.SettingGroup { @@ -50,7 +50,7 @@ class ScalaSettings extends Settings.SettingGroup { val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".") val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.") val language = MultiStringSetting("-language", "feature", "Enable one or more language features.") - val rewrite = OptionSetting[Patches.PatchedFiles]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax") + val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax") /** -X "Advanced" settings */ diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 47b0ae22d..d5ce455f3 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -21,7 +21,7 @@ import Constants._ import ScriptParsers._ import annotation.switch import util.DotClass -import rewrite.Patches.patch +import rewrite.Rewrites.patch object Parsers { diff --git a/src/dotty/tools/dotc/rewrite/Patches.scala b/src/dotty/tools/dotc/rewrite/Patches.scala deleted file mode 100644 index 7513ec7fa..000000000 --- a/src/dotty/tools/dotc/rewrite/Patches.scala +++ /dev/null @@ -1,87 +0,0 @@ -package dotty.tools.dotc -package rewrite - -import util.{SourceFile, Positions} -import Positions.Position -import core.Contexts.{Context, FreshContext} -import collection.mutable - -object Patches { - - private case class Patch(pos: Position, replacement: String) { - def delta = replacement.length - (pos.end - pos.start) - } - - class PatchedFiles extends mutable.HashMap[SourceFile, Patches] - - /** If -rewrite is set, record a patch that replaces the range - * given by `pos` in `source` by `replacement` - */ - def patch(source: SourceFile, pos: Position, replacement: String)(implicit ctx: Context): Unit = - ctx.settings.rewrite.value match { - case Some(pfs: PatchedFiles) => - pfs.get(source) match { - case Some(ps) => - ps.addPatch(pos, replacement) - case None => - pfs(source) = new Patches(source) - patch(source, pos, replacement) - } - case _ => - } - - /** If -rewrite is set, apply all patches and overwrite patched source files. - */ - def writeBack()(implicit ctx: Context) = - ctx.settings.rewrite.value match { - case Some(pfs: PatchedFiles) => - for (source <- pfs.keys) { - ctx.println(s"[patched file ${source.file.path}]") - pfs(source).writeBack() - } - case _ => - } -} - -class Patches(source: SourceFile) { - import Patches._ - - private val pbuf = new mutable.ListBuffer[Patch]() - - def addPatch(pos: Position, replacement: String): Unit = - pbuf += Patch(pos, replacement) - - def apply(cs: Array[Char]): Array[Char] = { - val delta = pbuf.map(_.delta).sum - val patches = pbuf.toList.sortBy(_.pos.start) - patches.iterator.sliding(2, 1).foreach(ps => - assert(ps(0).pos.end <= ps(1).pos.start, s"overlapping patches: ${ps(0)} and ${ps(1)}")) - val ds = new Array[Char](cs.length + delta) - def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = { - def copy(upTo: Int): Int = { - val untouched = upTo - inIdx - Array.copy(cs, inIdx, ds, outIdx, untouched) - outIdx + untouched - } - ps match { - case patch @ Patch(pos, replacement) :: ps1 => - val outNew = copy(pos.start) - replacement.copyToArray(ds, outNew) - loop(ps1, pos.end, outNew + replacement.length) - case Nil => - val outNew = copy(cs.length) - assert(outNew == ds.length, s"$outNew != ${ds.length}") - } - } - loop(patches, 0, 0) - ds - } - - def writeBack(): Unit = { - val out = source.file.output - val chars = apply(source.content) - val bytes = new String(chars).getBytes - out.write(bytes) - out.close() - } -} \ No newline at end of file diff --git a/src/dotty/tools/dotc/rewrite/Rewrites.scala b/src/dotty/tools/dotc/rewrite/Rewrites.scala new file mode 100644 index 000000000..27dafebcf --- /dev/null +++ b/src/dotty/tools/dotc/rewrite/Rewrites.scala @@ -0,0 +1,96 @@ +package dotty.tools.dotc +package rewrite + +import util.{SourceFile, Positions} +import Positions.Position +import core.Contexts.{Context, FreshContext} +import collection.mutable + +/** Handles rewriting of Scala2 files to Dotty */ +object Rewrites { + private class PatchedFiles extends mutable.HashMap[SourceFile, Patches] + + private case class Patch(pos: Position, replacement: String) { + def delta = replacement.length - (pos.end - pos.start) + } + + private class Patches(source: SourceFile) { + private val pbuf = new mutable.ListBuffer[Patch]() + + def addPatch(pos: Position, replacement: String): Unit = + pbuf += Patch(pos, replacement) + + def apply(cs: Array[Char]): Array[Char] = { + val delta = pbuf.map(_.delta).sum + val patches = pbuf.toList.sortBy(_.pos.start) + patches.iterator.sliding(2, 1).foreach(ps => + assert(ps(0).pos.end <= ps(1).pos.start, s"overlapping patches: ${ps(0)} and ${ps(1)}")) + val ds = new Array[Char](cs.length + delta) + def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = { + def copy(upTo: Int): Int = { + val untouched = upTo - inIdx + Array.copy(cs, inIdx, ds, outIdx, untouched) + outIdx + untouched + } + ps match { + case patch @ Patch(pos, replacement) :: ps1 => + val outNew = copy(pos.start) + replacement.copyToArray(ds, outNew) + loop(ps1, pos.end, outNew + replacement.length) + case Nil => + val outNew = copy(cs.length) + assert(outNew == ds.length, s"$outNew != ${ds.length}") + } + } + loop(patches, 0, 0) + ds + } + + def writeBack(): Unit = { + val out = source.file.output + val chars = apply(source.content) + val bytes = new String(chars).getBytes + out.write(bytes) + out.close() + } + } + + /** If -rewrite is set, record a patch that replaces the range + * given by `pos` in `source` by `replacement` + */ + def patch(source: SourceFile, pos: Position, replacement: String)(implicit ctx: Context): Unit = + ctx.settings.rewrite.value match { + case Some(rewrites: Rewrites) => + rewrites.patched.get(source) match { + case Some(ps) => + ps.addPatch(pos, replacement) + case None => + rewrites.patched(source) = new Patches(source) + patch(source, pos, replacement) + } + case _ => + } + + /** If -rewrite is set, apply all patches and overwrite patched source files. + */ + def writeBack()(implicit ctx: Context) = + ctx.settings.rewrite.value match { + case Some(rewrites: Rewrites) => + for (source <- rewrites.patched.keys) { + ctx.println(s"[patched file ${source.file.path}]") + rewrites.patched(source).writeBack() + } + case _ => + } +} + +/** A completely encapsulated class representing rewrite state, used + * as an optional setting. + */ +class Rewrites { + import Rewrites._ + private val patched = new PatchedFiles +} + + + -- cgit v1.2.3