From fbb13630d3c1dc9b8fdc503538d4ace5873f31c9 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 26 Apr 2013 13:30:59 -0700 Subject: Refactor DirectTest so java can be tested. There was no way to make a java compilation unit. Now there is. --- src/partest/scala/tools/partest/CompilerTest.scala | 2 +- src/partest/scala/tools/partest/DirectTest.scala | 28 ++++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/partest/scala/tools/partest/CompilerTest.scala b/src/partest/scala/tools/partest/CompilerTest.scala index d73d99bc89..848deef8c5 100644 --- a/src/partest/scala/tools/partest/CompilerTest.scala +++ b/src/partest/scala/tools/partest/CompilerTest.scala @@ -19,7 +19,7 @@ abstract class CompilerTest extends DirectTest { def check(source: String, unit: global.CompilationUnit): Unit lazy val global: Global = newCompiler() - lazy val units = compilationUnits(global)(sources: _ *) + lazy val units: List[global.CompilationUnit] = compilationUnits(global)(sources: _ *) import global._ import definitions._ diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index 483cb491a1..e2dac2fd55 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -7,7 +7,7 @@ package scala.tools.partest import scala.tools.nsc._ import io.Directory -import util.{BatchSourceFile, CommandLineParser} +import util.{ SourceFile, BatchSourceFile, CommandLineParser } import reporters.{Reporter, ConsoleReporter} /** A class for testing code which is embedded as a string. @@ -49,18 +49,32 @@ abstract class DirectTest extends App { def reporter(settings: Settings): Reporter = new ConsoleReporter(settings) - def newSources(sourceCodes: String*) = sourceCodes.toList.zipWithIndex map { - case (src, idx) => new BatchSourceFile("newSource" + (idx + 1), src) - } + private def newSourcesWithExtension(ext: String)(codes: String*): List[BatchSourceFile] = + codes.toList.zipWithIndex map { + case (src, idx) => new BatchSourceFile(s"newSource${idx + 1}.$ext", src) + } + + def newJavaSources(codes: String*) = newSourcesWithExtension("java")(codes: _*) + def newSources(codes: String*) = newSourcesWithExtension("scala")(codes: _*) + def compileString(global: Global)(sourceCode: String): Boolean = { withRun(global)(_ compileSources newSources(sourceCode)) !global.reporter.hasErrors } - def compilationUnits(global: Global)(sourceCodes: String*): List[global.CompilationUnit] = { - val units = withRun(global) { run => - run compileSources newSources(sourceCodes: _*) + + def javaCompilationUnits(global: Global)(sourceCodes: String*) = { + sourceFilesToCompiledUnits(global)(newJavaSources(sourceCodes: _*)) + } + + def sourceFilesToCompiledUnits(global: Global)(files: List[SourceFile]) = { + withRun(global) { run => + run compileSources files run.units.toList } + } + + def compilationUnits(global: Global)(sourceCodes: String*): List[global.CompilationUnit] = { + val units = sourceFilesToCompiledUnits(global)(newSources(sourceCodes: _*)) if (global.reporter.hasErrors) { global.reporter.flush() sys.error("Compilation failure.") -- cgit v1.2.3 From b2c67b328daeaf51eacdb0333db85a7287b5fe1f Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 26 Apr 2013 13:31:48 -0700 Subject: SI-7398 Add support for java8 default methods In classfile parser: mark symbols which represent interface methods yet have code attributes with new flag DEFAULTMETHOD. These must be kept distinct from regular method bodies so that an error can be issued when a regular concrete method is overridden without the override keyword, but not when the overridden method is a default. In java source parser: mark Modifiers of interface default methods with DEFAULTMETHOD flag. Writing the test was everything I dreamed, and more! However, % test/partest --debug test/files/run/t7398.scala Skipping java8-specific test under java version 1.7.0_21 testing: [...]/files/run/t7398.scala [ OK ] All of 1 tests were successful (elapsed time: 00:00:04) % test/partest --debug test/files/run/t7398.scala Attempting java8-specific test under java version 1.8.0-ea testing: [...]/files/run/t7398.scala [ OK ] All of 1 tests were successful (elapsed time: 00:00:13) --- bincompat-forward.whitelist.conf | 4 +++ .../scala/tools/nsc/javac/JavaParsers.scala | 6 ++++- .../nsc/symtab/classfile/ClassfileParser.scala | 6 +++++ .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- src/reflect/scala/reflect/internal/Flags.scala | 12 +++++---- test/files/run/t7398.scala | 31 ++++++++++++++++++++++ 6 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test/files/run/t7398.scala (limited to 'src') diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 82f094bed7..88eabd4f8c 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -426,6 +426,10 @@ filter { { matchName="scala.reflect.internal.TreeInfo.effectivePatternArity" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.ModifierFlags.DEFAULTMETHOD" + problemName=MissingMethodProblem } ] } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 8aa9b81a72..0779e648cd 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -420,6 +420,9 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { case FINAL => flags |= Flags.FINAL in.nextToken + case DEFAULT => + flags |= Flags.DEFAULTMETHOD + in.nextToken() case NATIVE => addAnnot(NativeAttr) in.nextToken @@ -544,8 +547,9 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val vparams = formalParams() if (!isVoid) rtpt = optArrayBrackets(rtpt) optThrows() + val bodyOk = !inInterface || (mods hasFlag Flags.DEFAULTMETHOD) val body = - if (!inInterface && in.token == LBRACE) { + if (bodyOk && in.token == LBRACE) { methodBody() } else { if (parentToken == AT && in.token == DEFAULT) { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index fb2301de65..0ae2b501f0 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -931,6 +931,12 @@ abstract class ClassfileParser { case pkg => pkg.fullName(File.separatorChar)+File.separator+srcfileLeaf } srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists) + case tpnme.CodeATTR => + if (sym.owner.isInterface) { + sym setFlag DEFAULTMETHOD + log(s"$sym in ${sym.owner} is a java8+ default method.") + } + in.skip(attrLen) case _ => in.skip(attrLen) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7f8aeceeec..396a2716d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -383,7 +383,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans overrideError("cannot be used here - classes can only override abstract types"); } else if (other.isEffectivelyFinal) { // (1.2) overrideError("cannot override final member"); - } else if (!other.isDeferred && !member.isAnyOverride && !member.isSynthetic) { // (*) + } else if (!other.isDeferred && !other.hasFlag(DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*) // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. if (isNeitherInClass && !(other.owner isSubClass member.owner)) diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 86cbba9c50..5ebe02d95d 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -59,9 +59,9 @@ import scala.collection.{ mutable, immutable } // 42: VBRIDGE // 43: VARARGS // 44: TRIEDCOOKING -// 45: -// 46: -// 47: +// 45: SYNCHRONIZED/M +// 46: ARTIFACT +// 47: DEFAULTMETHOD/M // 48: // 49: // 50: @@ -116,6 +116,8 @@ class ModifierFlags { final val LAZY = 1L << 31 // symbol is a lazy val. can't have MUTABLE unless transformed by typer final val PRESUPER = 1L << 37 // value is evaluated before super call final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit + // ARTIFACT at #46 in 2.11+ + final val DEFAULTMETHOD = 1L << 47 // symbol is a java default method // Overridden. def flagToString(flag: Long): String = "" @@ -239,7 +241,7 @@ class Flags extends ModifierFlags { */ final val ExplicitFlags = PRIVATE | PROTECTED | ABSTRACT | FINAL | SEALED | - OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY + OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY | DEFAULTMETHOD /** The two bridge flags */ final val BridgeFlags = BRIDGE | VBRIDGE @@ -421,7 +423,7 @@ class Flags extends ModifierFlags { case TRIEDCOOKING => "" // (1L << 44) case SYNCHRONIZED => "" // (1L << 45) case 0x400000000000L => "" // (1L << 46) - case 0x800000000000L => "" // (1L << 47) + case DEFAULTMETHOD => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) case 0x2000000000000L => "" // (1L << 49) case 0x4000000000000L => "" // (1L << 50) diff --git a/test/files/run/t7398.scala b/test/files/run/t7398.scala new file mode 100644 index 0000000000..e4090f7db3 --- /dev/null +++ b/test/files/run/t7398.scala @@ -0,0 +1,31 @@ +import scala.tools.partest._ + +object Test extends CompilerTest { + import global._ + + def javaVersion = scala.util.Properties.javaVersion + def isJavaEight = javaVersion startsWith "1.8" + // This way we auto-pass on non-java8 since there's nothing to check + override lazy val units = { + val res: List[CompilationUnit] = if (isJavaEight) javaCompilationUnits(global)(defaultMethodSource) else Nil + val word = if (isJavaEight) "Attempting" else "Skipping" + log(s"$word java8-specific test under java version $javaVersion") + res + } + + private def defaultMethodSource = """ +public interface Iterator { + boolean hasNext(); + E next(); + default void remove() { + throw new UnsupportedOperationException("remove"); + } + default void forEachRemaining(Consumer action) { + throw new UnsupportedOperationException("forEachRemaining"); + } +} + """ + + // We're only checking we can parse it. + def check(source: String, unit: global.CompilationUnit): Unit = () +} -- cgit v1.2.3