From e8af579a4442b3cda9ea82ddc1ce7f9bd6899418 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 28 Aug 2013 09:48:11 -0700 Subject: SI-7791 Line number table reflects underlying file Since positions ultimately know their ultimate positions in their ultimate source, use that line number, ultimately, when emitting line number table entries. It is possible, but possibly not useful, to emit both the actual (ultimate) line number and the nominal one. The `int`-valued line number of the `StackTraceElement` is "derived" from the attribute. In global, wrapping a `BatchSourceFile` as a `ScriptSource` happens in `compileSources` to facilitate testing. A `ScriptTest` facility is provided to facilitate testing the script facility. It is rather facile. --- src/compiler/scala/tools/nsc/Global.scala | 33 +++++++++++++--------- .../scala/tools/nsc/backend/jvm/GenASM.scala | 3 +- .../scala/tools/partest/ScriptTest.scala | 21 ++++++++++++++ .../scala/reflect/internal/util/SourceFile.scala | 5 ++++ test/files/run/t7791-script-linenums.check | 1 + test/files/run/t7791-script-linenums.scala | 16 +++++++++++ test/files/run/t7791-script-linenums.script | 8 ++++++ 7 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 src/partest-extras/scala/tools/partest/ScriptTest.scala create mode 100644 test/files/run/t7791-script-linenums.check create mode 100644 test/files/run/t7791-script-linenums.scala create mode 100644 test/files/run/t7791-script-linenums.script diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 3f2d759a6d..0e3b2993c7 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -352,9 +352,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // Here comes another one... override protected val enableTypeVarExperimentals = settings.Xexperimental.value - def getSourceFile(f: AbstractFile): BatchSourceFile = - if (settings.script.isSetByUser) ScriptSourceFile(f, reader read f) - else new BatchSourceFile(f, reader read f) + def getSourceFile(f: AbstractFile): BatchSourceFile = new BatchSourceFile(f, reader read f) def getSourceFile(name: String): SourceFile = { val f = AbstractFile.getFile(name) @@ -1490,20 +1488,23 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - /** Compile list of source files */ - def compileSources(sources: List[SourceFile]) { - // there is a problem already, e.g. a plugin was passed a bad option - if (reporter.hasErrors) - return + /** Compile list of source files, + * unless there is a problem already, + * such as a plugin was passed a bad option. + */ + def compileSources(sources: List[SourceFile]) = if (!reporter.hasErrors) { - // nothing to compile, but we should still report use of deprecated options - if (sources.isEmpty) { + def checkDeprecations() = { checkDeprecatedSettings(newCompilationUnit("")) reportCompileErrors() - return } - compileUnits(sources map (new CompilationUnit(_)), firstPhase) + val units = sources map scripted map (new CompilationUnit(_)) + + units match { + case Nil => checkDeprecations() // nothing to compile, report deprecated options + case _ => compileUnits(units, firstPhase) + } } def compileUnits(units: List[CompilationUnit], fromPhase: Phase): Unit = @@ -1605,12 +1606,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) catch { case ex: IOException => globalError(ex.getMessage()) } } + /** If this compilation is scripted, convert the source to a script source. */ + private def scripted(s: SourceFile) = s match { + case b: BatchSourceFile if settings.script.isSetByUser => ScriptSourceFile(b) + case _ => s + } + /** Compile abstract file until `globalPhase`, but at least * to phase "namer". */ def compileLate(file: AbstractFile) { if (!compiledFiles(file.path)) - compileLate(new CompilationUnit(getSourceFile(file))) + compileLate(new CompilationUnit(scripted(getSourceFile(file)))) } /** Compile abstract file until `globalPhase`, but at least to phase "namer". diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 3947db2dd4..edb1c55224 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -2316,7 +2316,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { lastLineNr = currentLineNr val lineLab = new asm.Label jmethod.visitLabel(lineLab) - lnEntries ::= LineNumberEntry(currentLineNr, lineLab) + val actual = iPos inUltimateSource iPos.source + lnEntries ::= LineNumberEntry(actual.line, lineLab) } } diff --git a/src/partest-extras/scala/tools/partest/ScriptTest.scala b/src/partest-extras/scala/tools/partest/ScriptTest.scala new file mode 100644 index 0000000000..24a4121b54 --- /dev/null +++ b/src/partest-extras/scala/tools/partest/ScriptTest.scala @@ -0,0 +1,21 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + */ + +package scala.tools.partest + +import scala.reflect.internal.util.ScalaClassLoader + +/** A `ScriptTest` is a `DirectTest` for which the code + * is the contents of a script file. + */ +abstract class ScriptTest extends DirectTest { + def testmain = "TestMain" + override def extraSettings = s"-usejavacp -Xscript $testmain" + def scriptPath = testPath changeExtension "script" + def code = scriptPath.toFile.slurp + def show() = { + compile() + ScalaClassLoader(getClass.getClassLoader).run(testmain, Seq.empty[String]) + } +} diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index ea4c9a9b68..6bb4cf3f0e 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -86,6 +86,11 @@ object ScriptSourceFile { stripped } + + def apply(underlying: BatchSourceFile) = { + val headerLen = headerLength(underlying.content) + new ScriptSourceFile(underlying, underlying.content drop headerLen, headerLen) + } } class ScriptSourceFile(underlying: BatchSourceFile, content: Array[Char], override val start: Int) extends BatchSourceFile(underlying.file, content) { diff --git a/test/files/run/t7791-script-linenums.check b/test/files/run/t7791-script-linenums.check new file mode 100644 index 0000000000..b7d969564a --- /dev/null +++ b/test/files/run/t7791-script-linenums.check @@ -0,0 +1 @@ +hello, scripted test diff --git a/test/files/run/t7791-script-linenums.scala b/test/files/run/t7791-script-linenums.scala new file mode 100644 index 0000000000..d89b8d4c63 --- /dev/null +++ b/test/files/run/t7791-script-linenums.scala @@ -0,0 +1,16 @@ + +import scala.tools.partest.ScriptTest + +object Test extends ScriptTest { + object ExceptionLine { + def unapply(e: Exception) = Some(e.getStackTrace()(0).getLineNumber) + } + override def show() = { + import util._ + Try(super.show()) match { + case Failure(ExceptionLine(7)) => () + case Failure(e) => e.printStackTrace() + case Success(_) => Console println "Expected error" + } + } +} diff --git a/test/files/run/t7791-script-linenums.script b/test/files/run/t7791-script-linenums.script new file mode 100644 index 0000000000..403dcc2d28 --- /dev/null +++ b/test/files/run/t7791-script-linenums.script @@ -0,0 +1,8 @@ +#!/bin/bash +exec ${SCALA_HOME}/bin/scala "$0" "$@" 2>&1 +!# + +Console println s"hello, scripted test" + +throw new RuntimeException("failing") // line 7 + -- cgit v1.2.3