Skip to content

Commit b8155a5

Browse files
authored
Merge pull request scala#6151 from retronym/merge/2.10.x-to-2.11.x-20171027
Merge 2.10.x + PR 6113 to 2.11.x [ci: last-only]
2 parents ad19744 + 517d170 commit b8155a5

File tree

12 files changed

+236
-86
lines changed

12 files changed

+236
-86
lines changed

bincompat-forward.whitelist.conf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,16 @@ filter {
628628
{
629629
matchName="scala.reflect.api.SerializedTypeTag.serialVersionUID"
630630
problemName=MissingFieldProblem
631+
},
632+
633+
{
634+
matchName="scala.reflect.io.ZipArchive.ensureDir"
635+
problemName=MissingMethodProblem
636+
},
637+
{
638+
matchName="scala.reflect.io.JavaToolsPlatformArchive"
639+
problemName=MissingClassProblem
631640
}
641+
632642
]
633643
}

src/compiler/scala/tools/ant/templates/tool-unix.tmpl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ while [[ $# -gt 0 ]]; do
160160
shift 2
161161
;;
162162
-nobootcp)
163-
unset usebootcp
163+
usebootcp="false"
164164
shift
165165
;;
166166
-usebootcp)
@@ -189,15 +189,22 @@ declare -a classpath_args
189189

190190
# default to the boot classpath for speed, except on cygwin/mingw/msys because
191191
# JLine on Windows requires a custom DLL to be loaded.
192-
unset usebootcp
193-
if [[ -z "$cygwin$mingw$msys" ]]; then
192+
if [[ "$usebootcp" != "false" && -z "$cygwin$mingw$msys" ]]; then
194193
usebootcp="true"
195194
fi
196195

197196
# If using the boot classpath, also pass an empty classpath
198197
# to java to suppress "." from materializing.
199-
if [[ -n $usebootcp ]]; then
198+
if [[ "$usebootcp" == "true" ]]; then
200199
classpath_args=("-Xbootclasspath/a:$TOOL_CLASSPATH" -classpath "\"\"")
200+
# Note that the version numbers go 1.7, 1.8, 9, 10, ...
201+
java_release="$(cat $JAVA_HOME/release | grep JAVA_VERSION)"
202+
if [[ ! "$java_release" =~ JAVA_VERSION=\"1\. ]]; then
203+
# Java 9 removed sun.boot.class.path, and the supposed replacement to at least see
204+
# the appended boot classpath (jdk.boot.class.path.append) is not visible.
205+
# So we have to pass a custom system property that PathResolver will find.
206+
classpath_args+=("-Dscala.boot.class.path=$TOOL_CLASSPATH")
207+
fi
201208
else
202209
classpath_args=(-classpath "$TOOL_CLASSPATH")
203210
fi

src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,17 +1692,17 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
16921692
def div(tk: TypeKind) { emitPrimitive(divOpcodes, tk) }
16931693
def rem(tk: TypeKind) { emitPrimitive(remOpcodes, tk) }
16941694

1695-
def invokespecial(owner: String, name: String, desc: String) {
1696-
jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false)
1695+
def invokespecial(owner: String, name: String, desc: String, itf: Boolean) {
1696+
jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, itf)
16971697
}
1698-
def invokestatic(owner: String, name: String, desc: String) {
1699-
jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false)
1698+
def invokestatic(owner: String, name: String, desc: String, itf: Boolean) {
1699+
jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, itf)
17001700
}
1701-
def invokeinterface(owner: String, name: String, desc: String) {
1702-
jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true)
1701+
def invokeinterface(owner: String, name: String, desc: String, itf: Boolean) {
1702+
jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, itf)
17031703
}
1704-
def invokevirtual(owner: String, name: String, desc: String) {
1705-
jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false)
1704+
def invokevirtual(owner: String, name: String, desc: String, itf: Boolean) {
1705+
jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, itf)
17061706
}
17071707

17081708
def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) }
@@ -2190,15 +2190,20 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
21902190
jmethod.visitFieldInsn(asm.Opcodes.PUTSTATIC, thisName, strMODULE_INSTANCE_FIELD, thisDescr)
21912191
}
21922192
}
2193+
val itf = if (receiver.isJavaDefined)
2194+
(if (receiver.isModuleClass) receiver.linkedClassOfClass else receiver).isJavaInterface
2195+
else
2196+
receiver.isInterface
2197+
def checkInterfaceTarget() = if (itf && settings.target.value != "jvm-1.8") reporter.error(call.pos, "Static methods in interface require -target:jvm-1.8")
21932198

21942199
style match {
2195-
case Static(true) => dbg("invokespecial"); jcode.invokespecial (jowner, jname, jtype)
2196-
case Static(false) => dbg("invokestatic"); jcode.invokestatic (jowner, jname, jtype)
2197-
case Dynamic if needsInterfaceCall(receiver) => dbg("invokinterface"); jcode.invokeinterface(jowner, jname, jtype)
2198-
case Dynamic => dbg("invokevirtual"); jcode.invokevirtual (jowner, jname, jtype)
2200+
case Static(true) => dbg("invokespecial"); jcode.invokespecial (jowner, jname, jtype, itf)
2201+
case Static(false) => dbg("invokestatic"); jcode.invokestatic (jowner, jname, jtype, itf); checkInterfaceTarget();
2202+
case Dynamic if needsInterfaceCall(receiver) => dbg("invokinterface"); jcode.invokeinterface(jowner, jname, jtype, itf)
2203+
case Dynamic => dbg("invokevirtual"); jcode.invokevirtual (jowner, jname, jtype, itf)
21992204
case SuperCall(_) =>
22002205
dbg("invokespecial")
2201-
jcode.invokespecial(jowner, jname, jtype)
2206+
jcode.invokespecial(jowner, jname, jtype, itf)
22022207
initModule()
22032208
}
22042209
} // end of genCode()'s genCallMethod()
@@ -2331,11 +2336,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
23312336
def genObjsInstr() = (instr: @unchecked) match {
23322337
case BOX(kind) =>
23332338
val MethodNameAndType(mname, mdesc) = jBoxTo(kind)
2334-
jcode.invokestatic(BoxesRunTime, mname, mdesc)
2339+
jcode.invokestatic(BoxesRunTime, mname, mdesc, itf = false)
23352340

23362341
case UNBOX(kind) =>
23372342
val MethodNameAndType(mname, mdesc) = jUnboxTo(kind)
2338-
jcode.invokestatic(BoxesRunTime, mname, mdesc)
2343+
jcode.invokestatic(BoxesRunTime, mname, mdesc, itf = false)
23392344

23402345
case NEW(REFERENCE(cls)) =>
23412346
val className = javaName(cls)
@@ -2373,7 +2378,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
23732378
/* Special handling to access native Array.clone() */
23742379
case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
23752380
val target: String = javaType(call.targetTypeKind).getInternalName
2376-
jcode.invokevirtual(target, "clone", mdesc_arrayClone)
2381+
jcode.invokevirtual(target, "clone", mdesc_arrayClone, itf = false)
23772382

23782383
case call @ CALL_METHOD(method, style) => genCallMethod(call)
23792384

@@ -2718,7 +2723,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
27182723
jcode.invokespecial(
27192724
StringBuilderClassName,
27202725
INSTANCE_CONSTRUCTOR_NAME,
2721-
mdesc_arglessvoid
2726+
mdesc_arglessvoid,
2727+
itf = false
27222728
)
27232729

27242730
case StringConcat(el) =>
@@ -2729,11 +2735,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
27292735
jcode.invokevirtual(
27302736
StringBuilderClassName,
27312737
"append",
2732-
asm.Type.getMethodDescriptor(StringBuilderType, Array(jtype): _*)
2738+
asm.Type.getMethodDescriptor(StringBuilderType, Array(jtype): _*),
2739+
itf = false
27332740
)
27342741

27352742
case EndConcat =>
2736-
jcode.invokevirtual(StringBuilderClassName, "toString", mdesc_toString)
2743+
jcode.invokevirtual(StringBuilderClassName, "toString", mdesc_toString, itf = false)
27372744

27382745
case _ => abort("Unimplemented primitive " + primitive)
27392746
}

src/compiler/scala/tools/util/PathResolver.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ package util
1010
import java.net.URL
1111
import scala.tools.reflect.WrappedProperties.AccessControl
1212
import scala.tools.nsc.Settings
13-
import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, JavaClassPath }
13+
import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, DirectoryClassPath, JavaClassPath }
1414
import scala.reflect.io.{ File, Directory, Path, AbstractFile }
1515
import scala.reflect.runtime.ReflectionUtils
1616
import ClassPath.{ JavaContext, DefaultJavaContext, join, split }
1717
import PartialFunction.condOpt
1818
import scala.language.postfixOps
1919
import scala.tools.nsc.classpath.{ AggregateFlatClassPath, ClassPathFactory, FlatClassPath, FlatClassPathFactory }
2020
import scala.tools.nsc.settings.ClassPathRepresentationType
21+
import scala.reflect.io.JavaToolsPlatformArchive
2122

2223
// Loosely based on the draft specification at:
2324
// https://wiki.scala-lang.org/display/SIW/Classpath
@@ -268,9 +269,18 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil
268269

269270
import classPathFactory._
270271

272+
private def javaBootClasspath = {
273+
val cp = classesInPath(javaBootClassPath)
274+
def okay = cp.exists(_.findClass("java/lang/Object.class").isDefined)
275+
val isFlat = settings.YclasspathImpl.value == ClassPathRepresentationType.Flat
276+
if (scala.util.Properties.isJavaAtLeast("9") && !isFlat && !okay)
277+
cp :+ new DirectoryClassPath(new JavaToolsPlatformArchive(), DefaultJavaContext).asInstanceOf[BaseClassPathType]
278+
else cp
279+
}
280+
271281
// Assemble the elements!
272282
def basis = List[Traversable[BaseClassPathType]](
273-
classesInPath(javaBootClassPath), // 1. The Java bootstrap class path.
283+
javaBootClasspath, // 1. The Java bootstrap class path.
274284
contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path.
275285
classesInExpandedPath(javaUserClassPath), // 3. The Java application class path.
276286
classesInPath(scalaBootClassPath), // 4. The Scala boot class path.

src/library/scala/util/Properties.scala

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -164,29 +164,56 @@ private[scala] trait PropertiesTrait {
164164
def scalaCmd = if (isWin) "scala.bat" else "scala"
165165
def scalacCmd = if (isWin) "scalac.bat" else "scalac"
166166

167+
167168
/** Compares the given specification version to the specification version of the platform.
168-
*
169-
* @param version a specification version of the form "major.minor"
170-
* @return `true` iff the specification version of the current runtime
171-
* is equal to or higher than the version denoted by the given string.
172-
* @throws NumberFormatException if the given string is not a version string
173-
*
174-
* @example {{{
175-
* // In this example, the runtime's Java specification is assumed to be at version 1.7.
176-
* isJavaAtLeast("1.6") // true
177-
* isJavaAtLeast("1.7") // true
178-
* isJavaAtLeast("1.8") // false
179-
* }}}
180-
*/
169+
*
170+
* @param version a specification version number (legacy forms acceptable)
171+
* @return `true` if the specification version of the current runtime
172+
* is equal to or higher than the version denoted by the given string.
173+
* @throws NumberFormatException if the given string is not a version string
174+
*
175+
* @example {{{
176+
* // In this example, the runtime's Java specification is assumed to be at version 8.
177+
* isJavaAtLeast("1.8") // true
178+
* isJavaAtLeast("8") // true
179+
* isJavaAtLeast("9") // false
180+
* isJavaAtLeast("9.1") // false
181+
* isJavaAtLeast("1.9") // throws
182+
* }}}
183+
*/
181184
def isJavaAtLeast(version: String): Boolean = {
182-
def parts(x: String) = {
183-
val i = x.indexOf('.')
184-
if (i < 0) throw new NumberFormatException("Not a version: " + x)
185-
(x.substring(0, i), x.substring(i+1, x.length))
185+
def versionOf(s: String, depth: Int): (Int, String) =
186+
s.indexOf('.') match {
187+
case 0 =>
188+
(-2, s.substring(1))
189+
case 1 if depth == 0 && s.charAt(0) == '1' =>
190+
val r0 = s.substring(2)
191+
val (v, r) = versionOf(r0, 1)
192+
val n = if (v > 8 || r0.isEmpty) -2 else v // accept 1.8, not 1.9 or 1.
193+
(n, r)
194+
case -1 =>
195+
val n = if (!s.isEmpty) s.toInt else if (depth == 0) -2 else 0
196+
(n, "")
197+
case i =>
198+
val r = s.substring(i + 1)
199+
val n = if (depth < 2 && r.isEmpty) -2 else s.substring(0, i).toInt
200+
(n, r)
201+
}
202+
def compareVersions(s: String, v: String, depth: Int): Int = {
203+
if (depth >= 3) 0
204+
else {
205+
val (sn, srest) = versionOf(s, depth)
206+
val (vn, vrest) = versionOf(v, depth)
207+
if (vn < 0) -2
208+
else if (sn < vn) -1
209+
else if (sn > vn) 1
210+
else compareVersions(srest, vrest, depth + 1)
211+
}
212+
}
213+
compareVersions(javaSpecVersion, version, 0) match {
214+
case -2 => throw new NumberFormatException(s"Not a version: $version")
215+
case i => i >= 0
186216
}
187-
val (v, _v) = parts(version)
188-
val (s, _s) = parts(javaSpecVersion)
189-
s.toInt >= v.toInt && _s.toInt >= _v.toInt
190217
}
191218

192219
// provide a main method so version info can be obtained by running this

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3252,7 +3252,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
32523252
override def isAnonOrRefinementClass = isAnonymousClass || isRefinementClass
32533253
override def isAnonymousClass = name containsName tpnme.ANON_CLASS_NAME
32543254
override def isConcreteClass = !(this hasFlag ABSTRACT | TRAIT)
3255-
override def isJavaInterface = hasAllFlags(JAVA | TRAIT)
3255+
override def isJavaInterface = hasAllFlags(JAVA | TRAIT) || hasJavaAnnotationFlag
32563256
override def isNestedClass = !isTopLevel
32573257
override def isNumericValueClass = definitions.isNumericValueClass(this)
32583258
override def isNumeric = isNumericValueClass

src/reflect/scala/reflect/io/AbstractFile.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package io
1111
import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream, ByteArrayOutputStream }
1212
import java.io.{ File => JFile }
1313
import java.net.URL
14+
1415
import scala.collection.mutable.ArrayBuffer
1516
import scala.reflect.internal.util.Statistics
1617

src/reflect/scala/reflect/io/ZipArchive.scala

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ package io
99

1010
import java.net.URL
1111
import java.io.{ IOException, InputStream, ByteArrayInputStream, FilterInputStream }
12-
import java.io.{ File => JFile }
13-
import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
12+
import java.io.{File => JFile}
13+
import java.util.zip.{ZipEntry, ZipFile, ZipInputStream}
1414
import java.util.jar.Manifest
15-
import scala.collection.{ immutable, mutable }
15+
16+
import scala.collection.{immutable, mutable}
1617
import scala.collection.convert.WrapAsScala.asScalaIterator
1718
import scala.annotation.tailrec
1819

@@ -64,7 +65,7 @@ import ZipArchive._
6465
abstract class ZipArchive(override val file: JFile) extends AbstractFile with Equals {
6566
self =>
6667

67-
override def underlyingSource = Some(this)
68+
override def underlyingSource: Some[AbstractFile] = Some(this)
6869
def isDirectory = true
6970
def lookupName(name: String, directory: Boolean) = unsupported()
7071
def lookupNameUnchecked(name: String, directory: Boolean) = unsupported()
@@ -78,7 +79,7 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
7879
sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) {
7980
// have to keep this name for compat with sbt's compiler-interface
8081
def getArchive: ZipFile = null
81-
override def underlyingSource = Some(self)
82+
override def underlyingSource: Some[AbstractFile] = Some(self)
8283
override def toString = self.path + "(" + path + ")"
8384
}
8485

@@ -94,7 +95,7 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
9495
}
9596
}
9697

97-
private def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry =
98+
protected def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry =
9899
//OPT inlined from getOrElseUpdate; saves ~50K closures on test run.
99100
// was:
100101
// dirs.getOrElseUpdate(path, {
@@ -290,3 +291,48 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
290291
}
291292
}
292293
}
294+
295+
296+
final class JavaToolsPlatformArchive() extends ZipArchive(new java.io.File(System.getProperty("java.home"))) {
297+
def iterator: Iterator[Entry] = {
298+
import javax.tools._
299+
import java.nio.charset.Charset
300+
import scala.collection.JavaConverters._
301+
val fileManager = ToolProvider.getSystemJavaCompiler.getStandardFileManager(new DiagnosticCollector, java.util.Locale.getDefault, Charset.defaultCharset)
302+
val root = new DirEntry("/")
303+
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
304+
val files: java.lang.Iterable[JavaFileObject] = fileManager.list(StandardLocation.PLATFORM_CLASS_PATH, "", java.util.EnumSet.of(JavaFileObject.Kind.CLASS), true)
305+
306+
for (f <- files.iterator().asScala) {
307+
val binaryName = fileManager.inferBinaryName(StandardLocation.PLATFORM_CLASS_PATH, f)
308+
val relativePath = binaryName.replace('.', '/') + ".class"
309+
val (packNameDotted, simpleName) = relativePath.lastIndexOf('/') match {
310+
case -1 => ("", binaryName)
311+
case i => (relativePath.substring(0, i + 1), relativePath.substring(i + 1))
312+
}
313+
val dir = if (packNameDotted.isEmpty) root else ensureDir(dirs, packNameDotted, null)
314+
class FileEntry() extends Entry(relativePath) {
315+
override def getArchive = null
316+
override def lastModified = 0L
317+
override def input = f.openInputStream()
318+
override def sizeOption = None
319+
}
320+
val entry = new FileEntry()
321+
dir.entries(simpleName) = entry
322+
}
323+
root.iterator
324+
}
325+
326+
def name = file.getName
327+
def path = file.getPath
328+
def input = unsupported
329+
def lastModified = file.lastModified
330+
331+
override def sizeOption = None
332+
override def canEqual(other: Any) = other.isInstanceOf[JavaToolsPlatformArchive]
333+
override def hashCode() = file.hashCode
334+
override def equals(that: Any) = that match {
335+
case x: JavaToolsPlatformArchive => true
336+
case _ => false
337+
}
338+
}

test/files/run/classfile-format-51.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ object Test extends DirectTest {
8181

8282
val test = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "test", s"()Ljava/lang/String;", null, null)
8383
test.visitCode()
84-
val bootstrapHandle = new Handle(H_INVOKESTATIC, invokerClassName, bootstrapMethodName, bootStrapMethodType)
84+
val bootstrapHandle = new Handle(H_INVOKESTATIC, invokerClassName, bootstrapMethodName, bootStrapMethodType, false)
8585
test.visitInvokeDynamicInsn("invoke", targetMethodType, bootstrapHandle)
8686
test.visitInsn(ARETURN)
8787
test.visitMaxs(1, 1)

test/files/run/t8852a.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import scala.tools.partest._
55
object Test extends CompilerTest {
66
import global._
77

8+
9+
override def extraSettings: String = super.extraSettings + " -target:jvm-1.8"
10+
811
override lazy val units: List[CompilationUnit] = {
912
// This test itself does not depend on JDK8.
1013
javaCompilationUnits(global)(staticMethodInInterface) ++

0 commit comments

Comments
 (0)