From 367407cb8998a1588af019557b2d25b80476980a Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 27 Apr 2017 15:49:25 +0200 Subject: [PATCH 01/12] Remove unnecessary abstraction Added in ced7214959, no longer needed since ICodeReader is gone. (cherry picked from commit e216e0ef0376c550846de974d5b71b39b92120b8) --- src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala | 2 -- .../scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 7 ++----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 2ad68f4d6203..8dbb110ce5ea 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -306,8 +306,6 @@ abstract class SymbolLoaders { private object classfileParser extends { val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable } with ClassfileParser { - override protected type ThisConstantPool = ConstantPool - override protected def newConstantPool: ThisConstantPool = new ConstantPool override protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = SymbolLoaders.this.lookupMemberAtTyperPhaseIfPossible(sym, name) /* diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 935a100effe8..1363f3108cf0 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -60,16 +60,13 @@ abstract class ClassfileParser { import scala.reflect.internal.ClassfileConstants._ import Flags._ - protected type ThisConstantPool <: ConstantPool - protected def newConstantPool: ThisConstantPool - protected var file: AbstractFile = _ // the class file protected var in: AbstractFileReader = _ // the class file reader protected var clazz: ClassSymbol = _ // the class symbol containing dynamic members protected var staticModule: ModuleSymbol = _ // the module symbol containing static members protected var instanceScope: Scope = _ // the scope of all instance definitions protected var staticScope: Scope = _ // the scope of all static definitions - protected var pool: ThisConstantPool = _ // the classfile's constant pool + protected var pool: ConstantPool = _ // the classfile's constant pool protected var isScala: Boolean = _ // does class file describe a scala class? protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation? protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info @@ -158,7 +155,7 @@ abstract class ClassfileParser { this.isScala = false parseHeader() - this.pool = newConstantPool + this.pool = new ConstantPool parseClass() } } From a44c34f461d0b2b3e42f73c623f8ee9e6318ac0c Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 28 Apr 2017 09:39:36 +0200 Subject: [PATCH 02/12] Invalidate symbols for artifact classfiles, refactor classfile parser No longer run the classfile parser on Scala generated classfiles that don't have a Scala signature (module classes, inner classes, etc). Various cleanups in the classfile parser, minimize the work performed on Scala classfiles. Before, the attributes section was parsed twice: once to find the ScalaSig attribute, the second time to find the ScalaSignature in the RuntimeVisibleAnnotations. Now everything happens in the first iteration. Also fixes a bug in the backend: classes ending in `$` did not get a ScalaSignature by mistake. They were filtered out by the name-based test that is supposed to identify module classes. (cherry picked from commit 3aea776ca1aa82c9de44cc6806dcdb242f3b40f8) --- .../tools/nsc/backend/jvm/BCodeHelpers.scala | 2 +- .../symtab/classfile/ClassfileParser.scala | 480 +++++++++--------- test/files/neg/moduleClassReference.check | 4 + test/files/neg/moduleClassReference.scala | 3 + test/files/neg/t7251.check | 2 +- 5 files changed, 249 insertions(+), 242 deletions(-) create mode 100644 test/files/neg/moduleClassReference.check create mode 100644 test/files/neg/moduleClassReference.scala diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index a6c8eb7f5229..674cc1415dae 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -415,7 +415,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic { */ def getAnnotPickle(jclassName: String, sym: Symbol): Option[AnnotationInfo] = { currentRun.symData get sym match { - case Some(pickle) if !sym.isModuleClass => + case Some(pickle) if !sym.isModuleClass => // pickles for module classes are in the companion / mirror class val scalaAnnot = { val sigBytes = ScalaSigBytes(pickle.bytes.take(pickle.writeIndex)) AnnotationInfo(sigBytes.sigAnnot, Nil, (nme.bytes, sigBytes) :: Nil) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 1363f3108cf0..e37ed2b16da0 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -68,7 +68,6 @@ abstract class ClassfileParser { protected var staticScope: Scope = _ // the scope of all static definitions protected var pool: ConstantPool = _ // the classfile's constant pool protected var isScala: Boolean = _ // does class file describe a scala class? - protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation? protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info protected var busy: Symbol = _ // lock to detect recursive reads protected var currentClass: Name = _ // JVM name of the current class @@ -439,21 +438,42 @@ abstract class ClassfileParser { } def parseClass() { - val jflags = readClassFlags() - val sflags = jflags.toScalaFlags - val nameIdx = u2 - currentClass = pool.getClassName(nameIdx) - - /* Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled. - * Updates the read pointer of 'in'. */ - def parseParents: List[Type] = { - if (isScala) { - u2 // skip superclass - val ifaces = u2 - in.bp += ifaces * 2 // .. and iface count interfaces - List(AnyRefTpe) // dummy superclass, will be replaced by pickled information + unpickleOrParseInnerClasses() + + val jflags = readClassFlags() + val classNameIndex = u2 + currentClass = pool.getClassName(classNameIndex) + + // Ensure that (top-level) classfiles are in the correct directory + val isTopLevel = !(currentClass containsChar '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532) + if (isTopLevel) { + val c = pool.getClassSymbol(classNameIndex) + // scala-dev#248: when a type alias (in a package object) shadows a class symbol, getClassSymbol returns a stub + // TODO: this also prevents the error when it would be useful (`mv a/C.class .`) + if (!c.isInstanceOf[StubSymbol] && c != clazz) mismatchError(c) + } + + // TODO: remove after the next 2.13 milestone + // A bug in the backend caused classes ending in `$` do get only a Scala marker attribute + // instead of a ScalaSig and a Signature annotaiton. This went unnoticed because isScalaRaw + // classes were parsed like Java classes. The below covers the cases in the std lib. + def isNothingOrNull = { + val n = clazz.fullName.toString + n == "scala.runtime.Nothing$" || n == "scala.runtime.Null$" + } + + if (isScala) { + () // We're done + } else if (isScalaRaw && !isNothingOrNull) { + val decls = clazz.enclosingPackage.info.decls + for (c <- List(clazz, staticModule, staticModule.moduleClass)) { + c.setInfo(NoType) + decls.unlink(c) } - else raiseLoaderLevel { + } else { + val sflags = jflags.toScalaFlags // includes JAVA + + def parseParents(): List[Type] = raiseLoaderLevel { val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } else pool.getSuperClass(u2).tpe_* val ifaceCount = u2 @@ -461,43 +481,35 @@ abstract class ClassfileParser { if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe superType :: ifaces } - } - - val isTopLevel = !(currentClass containsChar '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532) - if (isTopLevel) { - val c = pool.getClassSymbol(nameIdx) - // scala-dev#248: when a type alias (in a package object) shadows a class symbol, getClassSymbol returns a stub - if (!c.isInstanceOf[StubSymbol] && c != clazz) mismatchError(c) - } - addEnclosingTParams(clazz) - parseInnerClasses() // also sets the isScala / isScalaRaw flags, see r15956 - // get the class file parser to reuse scopes. - instanceScope = newScope - staticScope = newScope + addEnclosingTParams(clazz) - val classInfo = ClassInfoType(parseParents, instanceScope, clazz) - val staticInfo = ClassInfoType(List(), staticScope, moduleClass) + // Create scopes before calling `enterOwnInnerClasses` + instanceScope = newScope + staticScope = newScope + val staticInfo = ClassInfoType(List(), staticScope, moduleClass) + val classInfo = ClassInfoType(parseParents(), instanceScope, clazz) - if (!isScala && !isScalaRaw) enterOwnInnerClasses() - val curbp = in.bp - skipMembers() // fields - skipMembers() // methods - if (!isScala) { - clazz setFlag sflags - propagatePackageBoundary(jflags, clazz, staticModule, staticModule.moduleClass) clazz setInfo classInfo + clazz setFlag sflags moduleClass setInfo staticInfo + moduleClass setFlag JAVA staticModule setInfo moduleClass.tpe staticModule setFlag JAVA - staticModule.moduleClass setFlag JAVA + + propagatePackageBoundary(jflags, clazz, staticModule, moduleClass) + + val fieldsStartBp = in.bp + skipMembers() // fields + skipMembers() // methods + // attributes now depend on having infos set already parseAttributes(clazz, classInfo) def queueLoad() { - in.bp = curbp + in.bp = fieldsStartBp 0 until u2 foreach (_ => parseField()) sawPrivateConstructor = false 0 until u2 foreach (_ => parseMethod()) @@ -518,8 +530,7 @@ abstract class ClassfileParser { item() } } - } else - parseAttributes(clazz, classInfo) + } } /** Add type parameters of enclosing classes */ @@ -593,7 +604,7 @@ abstract class ClassfileParser { case MethodType(params, restpe) => // if this is a non-static inner class, remove the explicit outer parameter val paramsNoOuter = innerClasses getEntry currentClass match { - case Some(entry) if !isScalaRaw && !entry.jflags.isStatic => + case Some(entry) if !entry.jflags.isStatic => /* About `clazz.owner.hasPackageFlag` below: scala/bug#5957 * For every nested java class A$B, there are two symbols in the scala compiler. * 1. created by SymbolLoader, because of the existence of the A$B.class file, owner: package @@ -806,6 +817,9 @@ abstract class ClassfileParser { GenPolyType(ownTypeParams, tpe) } // sigToType + /** + * Only invoked for java classfiles. + */ def parseAttributes(sym: Symbol, symtype: Type, removedOuterParameter: Boolean = false) { var paramNames: ListBuffer[Name] = null // null means we didn't find any def convertTo(c: Constant, pt: Type): Constant = { @@ -814,32 +828,35 @@ abstract class ClassfileParser { else c convertTo pt } + def parseAttribute() { val attrName = readTypeName() val attrLen = u4 attrName match { case tpnme.SignatureATTR => - if (!isScala && !isScalaRaw) { - val sig = pool.getExternalName(u2) - val newType = sigToType(sym, sig) - sym.setInfo(newType) - } - else in.skip(attrLen) + val sig = pool.getExternalName(u2) + val newType = sigToType(sym, sig) + sym.setInfo(newType) + case tpnme.SyntheticATTR => sym.setFlag(SYNTHETIC | ARTIFACT) in.skip(attrLen) + case tpnme.BridgeATTR => sym.setFlag(BRIDGE | ARTIFACT) in.skip(attrLen) + case tpnme.DeprecatedATTR => val arg = Literal(Constant("see corresponding Javadoc for more information.")) sym.addAnnotation(DeprecatedAttr, arg, Literal(Constant(""))) in.skip(attrLen) + case tpnme.ConstantValueATTR => val c = pool.getConstant(u2) val c1 = convertTo(c, symtype) if (c1 ne null) sym.setInfo(ConstantType(c1)) else devWarning(s"failure to convert $c to $symtype") + case tpnme.MethodParametersATTR => def readParamNames(): Unit = { import scala.tools.asm.Opcodes.ACC_SYNTHETIC @@ -863,39 +880,15 @@ abstract class ClassfileParser { } } readParamNames() - case tpnme.ScalaSignatureATTR => - if (!isScalaAnnot) { - devWarning(s"symbol ${sym.fullName} has pickled signature in attribute") - unpickler.unpickle(in.buf, in.bp, clazz, staticModule, in.file.name) - } - in.skip(attrLen) - case tpnme.ScalaATTR => - isScalaRaw = true - // Attribute on methods of java annotation classes when that method has a default - case tpnme.AnnotationDefaultATTR => + + case tpnme.AnnotationDefaultATTR => // Methods of java annotation classes that have a default sym.addAnnotation(AnnotationDefaultAttr) in.skip(attrLen) - // Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME + case tpnme.RuntimeAnnotationATTR => - if (isScalaAnnot || !isScala) { - // For Scala classfiles we are only interested in the scala signature annotations. Other - // annotations should be skipped (the pickle contains the symbol's annotations). - // Skipping them also prevents some spurious warnings / errors related to scala/bug#7014, - // scala/bug#7551, pos/5165b - val scalaSigAnnot = parseAnnotations(onlyScalaSig = isScalaAnnot) - if (isScalaAnnot) scalaSigAnnot match { - case Some(san: AnnotationInfo) => - val bytes = - san.assocs.find({ _._1 == nme.bytes }).get._2.asInstanceOf[ScalaSigBytes].bytes - - unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.name) - case None => - throw new RuntimeException("Scala class file does not contain Scala annotation") - } - debuglog("[class] << " + sym.fullName + sym.annotationsString) - } - else - in.skip(attrLen) + val numAnnots = u2 + for (n <- 0 until numAnnots; annot <- parseAnnotation(u2)) + sym.addAnnotation(annot) // TODO 1: parse runtime visible annotations on parameters // case tpnme.RuntimeParamAnnotationATTR @@ -903,7 +896,7 @@ abstract class ClassfileParser { // TODO 2: also parse RuntimeInvisibleAnnotation / RuntimeInvisibleParamAnnotation, // i.e. java annotations with RetentionPolicy.CLASS? - case tpnme.ExceptionsATTR if (!isScala) => + case tpnme.ExceptionsATTR => parseExceptions(attrLen) case tpnme.SourceFileATTR => @@ -927,36 +920,40 @@ abstract class ClassfileParser { } srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists) } else in.skip(attrLen) + case tpnme.CodeATTR => if (sym.owner.isInterface) { sym setFlag JAVA_DEFAULTMETHOD log(s"$sym in ${sym.owner} is a java8+ default method.") } in.skip(attrLen) + case _ => in.skip(attrLen) } } - def skipAnnotArg(): Unit = { - u1 match { - case STRING_TAG | BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | - INT_TAG | LONG_TAG | FLOAT_TAG | DOUBLE_TAG | CLASS_TAG => - in.skip(2) - - case ENUM_TAG => - in.skip(4) - - case ARRAY_TAG => - val num = u2 - for (i <- 0 until num) skipAnnotArg() - - case ANNOTATION_TAG => - parseAnnotation(u2, onlyScalaSig = true) + /* + * Parse the "Exceptions" attribute which denotes the exceptions + * thrown by a method. + */ + def parseExceptions(len: Int) { + val nClasses = u2 + for (n <- 0 until nClasses) { + // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (scala/bug#7065) + val cls = pool.getClassSymbol(u2) + // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation + // and that method requires Symbol to be forced to give the right answers, see scala/bug#7107 for details + cls.initialize + sym.addThrowsAnnotation(cls) } } - def parseAnnotArg: Option[ClassfileAnnotArg] = { + // begin parseAttributes + for (i <- 0 until u2) parseAttribute() + } + + def parseAnnotArg(): Option[ClassfileAnnotArg] = { val tag = u1 val index = u2 tag match { @@ -981,140 +978,52 @@ abstract class ClassfileParser { None } - case ARRAY_TAG => - val arr = new ArrayBuffer[ClassfileAnnotArg]() - var hasError = false - for (i <- 0 until index) - parseAnnotArg match { - case Some(c) => arr += c - case None => hasError = true - } - if (hasError) None - else Some(ArrayAnnotArg(arr.toArray)) - case ANNOTATION_TAG => - parseAnnotation(index, onlyScalaSig = false) map (NestedAnnotArg(_)) - } - } - - def parseScalaSigBytes: Option[ScalaSigBytes] = { - val tag = u1 - assert(tag == STRING_TAG, tag) - Some(ScalaSigBytes(pool getBytes u2)) - } - - def parseScalaLongSigBytes: Option[ScalaSigBytes] = { - val tag = u1 - assert(tag == ARRAY_TAG, tag) - val stringCount = u2 - val entries = - for (i <- 0 until stringCount) yield { - val stag = u1 - assert(stag == STRING_TAG, stag) - u2 - } - Some(ScalaSigBytes(pool.getBytes(entries.toList))) - } - - // TODO scala/bug#9296 duplicated code, refactor - /* Parse and return a single annotation. If it is malformed, - * return None. - */ - def parseAnnotation(attrNameIndex: Int, onlyScalaSig: Boolean): Option[AnnotationInfo] = try { - val attrType = pool.getType(attrNameIndex) - val nargs = u2 - val nvpairs = new ListBuffer[(Name, ClassfileAnnotArg)] - var hasError = false - for (i <- 0 until nargs) { - val name = readName() - // The "bytes: String" argument of the ScalaSignature attribute is parsed specially so that it is - // available as an array of bytes (the pickled Scala signature) instead of as a string. The pickled signature - // is encoded as a string because of limitations in the Java class file format. - if ((attrType == ScalaSignatureAnnotation.tpe) && (name == nme.bytes)) - parseScalaSigBytes match { - case Some(c) => nvpairs += ((name, c)) + case ARRAY_TAG => + val arr = new ArrayBuffer[ClassfileAnnotArg]() + var hasError = false + for (i <- 0 until index) + parseAnnotArg() match { + case Some(c) => arr += c case None => hasError = true } - else if ((attrType == ScalaLongSignatureAnnotation.tpe) && (name == nme.bytes)) - parseScalaLongSigBytes match { - case Some(c) => nvpairs += ((name, c)) - case None => hasError = true - } - else - if (onlyScalaSig) skipAnnotArg() - else parseAnnotArg match { - case Some(c) => nvpairs += ((name, c)) - case None => hasError = true - } - } - if (hasError) None - else Some(AnnotationInfo(attrType, List(), nvpairs.toList)) - } catch { - case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found - case NonFatal(ex) => - // We want to be robust when annotations are unavailable, so the very least - // we can do is warn the user about the exception - // There was a reference to ticket 1135, but that is outdated: a reference to a class not on - // the classpath would *not* end up here. A class not found is signaled - // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), - // and that should never be swallowed silently. - warning(s"Caught: $ex while parsing annotations in ${in.file}") - if (settings.debug) ex.printStackTrace() - None // ignore malformed annotations + if (hasError) None + else Some(ArrayAnnotArg(arr.toArray)) + case ANNOTATION_TAG => + parseAnnotation(index) map (NestedAnnotArg(_)) } + } - /* - * Parse the "Exceptions" attribute which denotes the exceptions - * thrown by a method. - */ - def parseExceptions(len: Int) { - val nClasses = u2 - for (n <- 0 until nClasses) { - // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (scala/bug#7065) - val cls = pool.getClassSymbol(u2) - // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation - // and that method requires Symbol to be forced to give the right answers, see scala/bug#7107 for details - cls.initialize - sym.addThrowsAnnotation(cls) - } - } - /* Parse a sequence of annotations and attaches them to the - * current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */ - def parseAnnotations(onlyScalaSig: Boolean): Option[AnnotationInfo] = { - val nAttr = u2 - var scalaSigAnnot: Option[AnnotationInfo] = None - for (n <- 0 until nAttr) parseAnnotation(u2, onlyScalaSig) match { - case Some(scalaSig) if scalaSig.atp == ScalaSignatureAnnotation.tpe => - scalaSigAnnot = Some(scalaSig) - case Some(scalaSig) if scalaSig.atp == ScalaLongSignatureAnnotation.tpe => - scalaSigAnnot = Some(scalaSig) - case Some(annot) => - sym.addAnnotation(annot) - case None => + // TODO scala/bug#9296 duplicated code, refactor + /** + * Parse and return a single annotation. If it is malformed, return None. + */ + def parseAnnotation(attrNameIndex: Int): Option[AnnotationInfo] = try { + val attrType = pool.getType(attrNameIndex) + val nargs = u2 + val nvpairs = new ListBuffer[(Name, ClassfileAnnotArg)] + var hasError = false + for (i <- 0 until nargs) { + val name = readName() + parseAnnotArg() match { + case Some(c) => nvpairs += ((name, c)) + case None => hasError = true } - scalaSigAnnot } - - def addParamNames(): Unit = - if ((paramNames ne null) && sym.hasRawInfo && sym.isMethod) { - val params = sym.rawInfo.params - foreach2(paramNames.toList, params) { - case (nme.NO_NAME, _) => // param was ACC_SYNTHETIC; ignore - case (name, param) => - param.resetFlag(SYNTHETIC) - param.name = name - } - devWarningIf(!sameLength(paramNames.toList, params)) { - // there's not anything we can do, but it's slightly worrisome - sm"""MethodParameters length mismatch while parsing $sym: - | rawInfo.params: ${sym.rawInfo.params} - | MethodParameters: ${paramNames.toList}""" - } - } - - // begin parseAttributes - for (i <- 0 until u2) parseAttribute() - addParamNames() + if (hasError) None + else Some(AnnotationInfo(attrType, List(), nvpairs.toList)) + } catch { + case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found + case NonFatal(ex) => + // We want to be robust when annotations are unavailable, so the very least + // we can do is warn the user about the exception + // There was a reference to ticket 1135, but that is outdated: a reference to a class not on + // the classpath would *not* end up here. A class not found is signaled + // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), + // and that should never be swallowed silently. + warning(s"Caught: $ex while parsing annotations in ${in.file}") + if (settings.debug) ex.printStackTrace() + None // ignore malformed annotations } /** Apply `@native`/`@transient`/`@volatile` annotations to `sym`, @@ -1190,38 +1099,129 @@ abstract class ClassfileParser { } } - /** Parse inner classes. Expects `in.bp` to point to the superclass entry. - * Restores the old `bp`. + /** + * Either + * - set `isScala` and invoke the unpickler, or + * - set `isScalaRaw`, or + * - parse inner classes (for Java classfiles) + * + * Expects `in.bp` to point to the `access_flags` entry, restores the old `bp`. */ - def parseInnerClasses() { + def unpickleOrParseInnerClasses() { val oldbp = in.bp + in.skip(4) // access_flags, this_class skipSuperclasses() skipMembers() // fields skipMembers() // methods - val attrs = u2 - for (i <- 0 until attrs) { + + var innersStart = -1 + var runtimeAnnotStart = -1 + + val numAttrs = u2 + var i = 0 + while (i < numAttrs) { val attrName = readTypeName() val attrLen = u4 attrName match { case tpnme.ScalaSignatureATTR => isScala = true - val pbuf = new PickleBuffer(in.buf, in.bp, in.bp + attrLen) - pbuf.readNat(); pbuf.readNat() - if (pbuf.readNat == 0) // a scala signature attribute with no entries means that the actual scala signature - isScalaAnnot = true // is in a ScalaSignature annotation. - in.skip(attrLen) + if (runtimeAnnotStart != -1) i = numAttrs case tpnme.ScalaATTR => isScalaRaw = true - case tpnme.InnerClassesATTR if !isScala => - val entries = u2 - for (i <- 0 until entries) { - val innerIndex, outerIndex, nameIndex = u2 - val jflags = readInnerClassFlags() - if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) - innerClasses add InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) + i = numAttrs + case tpnme.InnerClassesATTR => + innersStart = in.bp + case tpnme.RuntimeAnnotationATTR => + runtimeAnnotStart = in.bp + if (isScala) i = numAttrs + case _ => + } + in.skip(attrLen) + i += 1 + } + + if (isScala) { + def parseScalaSigBytes(): Array[Byte] = { + val tag = u1 + assert(tag == STRING_TAG, tag) + pool.getBytes(u2) + } + + def parseScalaLongSigBytes(): Array[Byte] = { + val tag = u1 + assert(tag == ARRAY_TAG, tag) + val stringCount = u2 + val entries = + for (i <- 0 until stringCount) yield { + val stag = u1 + assert(stag == STRING_TAG, stag) + u2 } + pool.getBytes(entries.toList) + } + + def checkScalaSigAnnotArg() = { + val numArgs = u2 + assert(numArgs == 1, s"ScalaSignature has $numArgs arguments") + val name = readName() + assert(name == nme.bytes, s"ScalaSignature argument has name $name") + } + + def skipAnnotArg(): Unit = u1 match { + case STRING_TAG | BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | + INT_TAG | LONG_TAG | FLOAT_TAG | DOUBLE_TAG | CLASS_TAG => + in.skip(2) + + case ENUM_TAG => + in.skip(4) + + case ARRAY_TAG => + val num = u2 + for (i <- 0 until num) skipAnnotArg() + + case ANNOTATION_TAG => + in.skip(2) // type + skipAnnotArgs() + } + + def skipAnnotArgs() = { + val numArgs = u2 + for (i <- 0 until numArgs) { + in.skip(2) + skipAnnotArg() + } + } + + val SigTpe = ScalaSignatureAnnotation.tpe + val LongSigTpe = ScalaLongSignatureAnnotation.tpe + + assert(runtimeAnnotStart != -1, s"No RuntimeVisibleAnnotations in classfile with ScalaSignature attribute: $clazz") + in.bp = runtimeAnnotStart + val numAnnots = u2 + var i = 0 + var bytes: Array[Byte] = null + while (i < numAnnots && bytes == null) pool.getType(u2) match { + case SigTpe => + checkScalaSigAnnotArg() + bytes = parseScalaSigBytes() + case LongSigTpe => + checkScalaSigAnnotArg() + bytes = parseScalaLongSigBytes() case _ => - in.skip(attrLen) + skipAnnotArgs() + } + + AnyRefClass // Force scala.AnyRef, otherwise we get "error: Symbol AnyRef is missing from the classpath" + assert(bytes != null, s"No Scala(Long)Signature annotation in classfile with ScalaSignature attribute: $clazz") + unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.name) + } else if (!isScalaRaw && innersStart != -1) { + in.bp = innersStart + val entries = u2 + for (i <- 0 until entries) { + val innerIndex, outerIndex, nameIndex = u2 + val jflags = readInnerClassFlags() + if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) + innerClasses add InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) } } in.bp = oldbp diff --git a/test/files/neg/moduleClassReference.check b/test/files/neg/moduleClassReference.check new file mode 100644 index 000000000000..1f16aeb2509f --- /dev/null +++ b/test/files/neg/moduleClassReference.check @@ -0,0 +1,4 @@ +moduleClassReference.scala:2: error: not found: value Predef$ + def foo = Predef$.MODULE$ == Predef + ^ +one error found diff --git a/test/files/neg/moduleClassReference.scala b/test/files/neg/moduleClassReference.scala new file mode 100644 index 000000000000..dbf688840e2f --- /dev/null +++ b/test/files/neg/moduleClassReference.scala @@ -0,0 +1,3 @@ +object Test { + def foo = Predef$.MODULE$ == Predef +} diff --git a/test/files/neg/t7251.check b/test/files/neg/t7251.check index 33fdafc2ee1e..a17e710d367e 100644 --- a/test/files/neg/t7251.check +++ b/test/files/neg/t7251.check @@ -1,4 +1,4 @@ -B_2.scala:5: error: class s.Outer$Triple$ is not a value +B_2.scala:5: error: object Outer$Triple$ is not a member of package s println( s.Outer$Triple$ ) ^ one error found From 421db54d9cee4204b7801fcde9fab8ecc27bd2e6 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Mar 2019 14:55:22 +1000 Subject: [PATCH 03/12] Avoid Names for descriptors, generic sigs, and string constants We can just keep these are short-lived Strings, rather than interning them into the Name table for the entire lifetime of Global. (cherry picked from commit 688bf0fcae4ced47fa440def73e3940005c841b1) --- .../symtab/classfile/ClassfileParser.scala | 108 ++++++++++-------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index e37ed2b16da0..d9e43801fcbb 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -96,7 +96,7 @@ abstract class ClassfileParser { private def readMethodFlags() = JavaAccFlags methodFlags u2 private def readFieldFlags() = JavaAccFlags fieldFlags u2 private def readTypeName() = readName().toTypeName - private def readName() = pool getName u2 + private def readName() = pool.getName(u2).name private def readType() = pool getType u2 private object unpickler extends scala.reflect.internal.pickling.UnPickler { @@ -176,7 +176,7 @@ abstract class ClassfileParser { protected val len = u2 protected val starts = new Array[Int](len) protected val values = new Array[AnyRef](len) - protected val internalized = new Array[Name](len) + protected val internalized = new Array[NameOrString](len) { var i = 1 while (i < starts.length) { @@ -207,15 +207,22 @@ abstract class ClassfileParser { else this errorBadTag start } + class NameOrString(val value: String) { + private var _name: Name = null + def name: Name = { + if (_name eq null) _name = TermName(value) + _name + } + } /** Return the name found at given index. */ - def getName(index: Int): Name = ( + def getName(index: Int): NameOrString = ( if (index <= 0 || len <= index) errorBadIndex(index) else values(index) match { - case name: Name => name + case name: NameOrString => name case _ => val start = firstExpecting(index, CONSTANT_UTF8) val len = in.getChar(start).toInt - recordAtIndex(TermName(fromMUTF8(in.buf, start, len + 2)), index) + recordAtIndex(new NameOrString(fromMUTF8(in.buf, start, len + 2)), index) } ) @@ -223,12 +230,12 @@ abstract class ClassfileParser { new DataInputStream(new ByteArrayInputStream(bytes, offset, len)).readUTF /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ - def getExternalName(index: Int): Name = { + def getExternalName(index: Int): NameOrString = { if (index <= 0 || len <= index) errorBadIndex(index) if (internalized(index) == null) - internalized(index) = getName(index).replace('/', '.') + internalized(index) = new NameOrString(getName(index).value.replace('/', '.')) internalized(index) } @@ -238,7 +245,7 @@ abstract class ClassfileParser { values(index) match { case sym: Symbol => sym case _ => - val result = getClassName(index) match { + val result = getClassName(index).name match { case name if nme.isModuleName(name) => rootMirror getModuleByName name.dropModule case name => classNameToSymbol(name) } @@ -249,7 +256,7 @@ abstract class ClassfileParser { /** Return the external name of the class info structure found at 'index'. * Use 'getClassSymbol' if the class is sure to be a top-level class. */ - def getClassName(index: Int): Name = { + def getClassName(index: Int): NameOrString = { val start = firstExpecting(index, CONSTANT_CLASS) getExternalName((in getChar start).toInt) } @@ -268,14 +275,14 @@ abstract class ClassfileParser { val start = firstExpecting(index, CONSTANT_NAMEANDTYPE) val name = getName(in.getChar(start).toInt) // create a dummy symbol for method types - val dummy = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) + val dummy = ownerTpe.typeSymbol.newMethod(name.name.toTermName, ownerTpe.typeSymbol.pos) val tpe = getType(dummy, in.getChar(start + 2).toInt) // fix the return type, which is blindly set to the class currently parsed val restpe = tpe match { - case MethodType(formals, _) if name == nme.CONSTRUCTOR => MethodType(formals, ownerTpe) - case _ => tpe + case MethodType(formals, _) if name.name == nme.CONSTRUCTOR => MethodType(formals, ownerTpe) + case _ => tpe } - ((name, restpe)) + ((name.name, restpe)) } } @@ -290,21 +297,21 @@ abstract class ClassfileParser { case cls: Symbol => cls.tpe_* case _ => val name = getClassName(index) - name charAt 0 match { - case ARRAY_TAG => recordAtIndex(sigToType(null, name), index) - case _ => recordAtIndex(classNameToSymbol(name), index).tpe_* + name.value.charAt(0) match { + case ARRAY_TAG => recordAtIndex(sigToType(null, name.value), index) + case _ => recordAtIndex(classNameToSymbol(name.name), index).tpe_* } } } def getType(index: Int): Type = getType(null, index) - def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index)) + def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index).value) def getSuperClass(index: Int): Symbol = if (index == 0) AnyClass else getClassSymbol(index) // the only classfile that is allowed to have `0` in the super_class is java/lang/Object (see jvm spec) private def createConstant(index: Int): Constant = { val start = starts(index) Constant((in.buf(start).toInt: @switch) match { - case CONSTANT_STRING => getName(in.getChar(start + 1).toInt).toString + case CONSTANT_STRING => getName(in.getChar(start + 1).toInt).value case CONSTANT_INTEGER => in.getInt(start + 1) case CONSTANT_FLOAT => in.getFloat(start + 1) case CONSTANT_LONG => in.getLong(start + 1) @@ -442,7 +449,7 @@ abstract class ClassfileParser { val jflags = readClassFlags() val classNameIndex = u2 - currentClass = pool.getClassName(classNameIndex) + currentClass = pool.getClassName(classNameIndex).name // Ensure that (top-level) classfiles are in the correct directory val isTopLevel = !(currentClass containsChar '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532) @@ -643,7 +650,8 @@ abstract class ClassfileParser { } } - private def sigToType(sym: Symbol, sig: Name): Type = { + private def sigToType(sym: Symbol, sig: String): Type = { + val sigChars = sig.toCharArray var index = 0 val end = sig.length def accept(ch: Char) { @@ -653,7 +661,7 @@ abstract class ClassfileParser { def subName(isDelimiter: Char => Boolean): Name = { val start = index while (!isDelimiter(sig.charAt(index))) { index += 1 } - sig.subName(start, index) + newTermName(sigChars, start, index - start) } def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = { val tag = sig.charAt(index); index += 1 @@ -835,7 +843,7 @@ abstract class ClassfileParser { attrName match { case tpnme.SignatureATTR => val sig = pool.getExternalName(u2) - val newType = sigToType(sym, sig) + val newType = sigToType(sym, sig.value) sym.setInfo(newType) case tpnme.SyntheticATTR => @@ -872,7 +880,7 @@ abstract class ClassfileParser { val access = u2 val name = - if ((access & ACC_SYNTHETIC) == 0) rawname.encode + if ((access & ACC_SYNTHETIC) == 0) rawname.name.encode else nme.NO_NAME paramNames += name @@ -953,30 +961,30 @@ abstract class ClassfileParser { for (i <- 0 until u2) parseAttribute() } - def parseAnnotArg(): Option[ClassfileAnnotArg] = { - val tag = u1 - val index = u2 - tag match { - case STRING_TAG => - Some(LiteralAnnotArg(Constant(pool.getName(index).toString))) - case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | INT_TAG | - LONG_TAG | FLOAT_TAG | DOUBLE_TAG => - Some(LiteralAnnotArg(pool.getConstant(index))) - case CLASS_TAG => - Some(LiteralAnnotArg(Constant(pool.getType(index)))) - case ENUM_TAG => - val t = pool.getType(index) - val n = readName() - val module = t.typeSymbol.companionModule - val s = module.info.decls.lookup(n) - if (s != NoSymbol) Some(LiteralAnnotArg(Constant(s))) - else { - warning( - sm"""While parsing annotations in ${in.file}, could not find $n in enum ${module.nameString}. - |This is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (scala/bug#7014).""" - ) - None - } + def parseAnnotArg(): Option[ClassfileAnnotArg] = { + val tag = u1 + val index = u2 + tag match { + case STRING_TAG => + Some(LiteralAnnotArg(Constant(pool.getName(index).value))) + case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | INT_TAG | + LONG_TAG | FLOAT_TAG | DOUBLE_TAG => + Some(LiteralAnnotArg(pool.getConstant(index))) + case CLASS_TAG => + Some(LiteralAnnotArg(Constant(pool.getType(index)))) + case ENUM_TAG => + val t = pool.getType(index) + val n = readName() + val module = t.typeSymbol.companionModule + val s = module.info.decls.lookup(n) + if (s != NoSymbol) Some(LiteralAnnotArg(Constant(s))) + else { + warning( + sm"""While parsing annotations in ${in.file}, could not find $n in enum ${module.nameString}. + |This is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (scala/bug#7014).""" + ) + None + } case ARRAY_TAG => val arr = new ArrayBuffer[ClassfileAnnotArg]() @@ -1229,9 +1237,9 @@ abstract class ClassfileParser { /** An entry in the InnerClasses attribute of this class file. */ case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: JavaAccFlags) { - def externalName = pool getClassName external - def outerName = pool getClassName outer - def originalName = pool getName name + def externalName = pool.getClassName(external).name + def outerName = pool.getClassName(outer).name + def originalName = pool.getName(name).name def isModule = originalName.isTermName def scope = if (jflags.isStatic) staticScope else instanceScope def enclosing = if (jflags.isStatic) enclModule else enclClass From 1c710ced22a0855cbc9ece1b410816c5958bdc02 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Mar 2019 15:01:15 +1000 Subject: [PATCH 04/12] Eagerly read from the constant pool as a basis for lazy types java class/method I've used lazy types for field/method/class infos, which is analagous to what we do in `Unpickler` for scala originated types. We read all data needed by the inner class table and the type completers from the pool eagerly, but still be lazy about interning strings to Names and completion of the field/method types themselves. This fixes some long standing spurious cyclic errors: Manually tested with: ``` $ scalac -cp $(coursier fetch -q -p com.datastax.cassandra:dse-driver:1.0.0) test.scala test.scala:2: error: illegal cyclic reference involving class Cluster new com.datastax.driver.dse.DseCluster.Builder() ^ one error found $ /code/scala/build/quick/bin/scalac -cp $(coursier fetch -q -p com.datastax.cassandra:dse-driver:1.0.0) test.scala $ cat test.scala class Test { new com.datastax.driver.dse.DseCluster.Builder() } ``` ``` class Test { new com.datastax.driver.dse.DseCluster.Builder() } ``` It subsumes `-Ybreak-cycles`. I was to compile `pos/cycle{-jsoup,}` without the experimental `-Ybreak-cycles`. I've removed the entire `-Ybreak-cycles` option and supporting code. It also subsumes the existing implementation related to cycle avoidance (`queueLoad` / `raiseLoaderLevel`.) Descriptor, signature, constant, and param names are made available to the field/method lazy completer which can use them if and when the symbol is completed. After classfile parsing the constant pool is discarded (some entries are of course captured by the lazy type completers.) Fixes scala/bug#3809 (cherry picked from commit 4554e02d72097e6e140ba97f2b45620f251fa238) --- .../symtab/classfile/AbstractFileReader.scala | 35 +- .../classfile/ByteBufferDataReader.scala | 89 ++++ .../symtab/classfile/ClassfileParser.scala | 391 ++++++++++-------- .../nsc/symtab/classfile/DataReader.scala | 68 +++ .../scala/reflect/internal/Definitions.scala | 5 +- .../scala/reflect/internal/Symbols.scala | 9 +- .../scala/reflect/internal/Types.scala | 48 +-- .../scala/reflect/io/AbstractFile.scala | 2 + src/reflect/scala/reflect/io/PlainFile.scala | 18 + .../reflect/runtime/JavaUniverseForce.scala | 1 - test/files/jvm/throws-annot-from-java.check | 8 +- .../jvm/throws-annot-from-java/Test_3.scala | 4 +- .../t7008-scala-defined/Impls_Macros_2.scala | 2 + test/files/run/t7008/Impls_Macros_2.scala | 2 + test/files/run/t7455/Test.scala | 2 +- 15 files changed, 456 insertions(+), 228 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala create mode 100644 src/compiler/scala/tools/nsc/symtab/classfile/DataReader.scala diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala index a8d673663e8d..26e7f9d2060d 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/AbstractFileReader.scala @@ -14,8 +14,10 @@ package scala.tools.nsc package symtab package classfile -import java.lang.Float.intBitsToFloat +import java.io.{ByteArrayInputStream, DataInputStream} import java.lang.Double.longBitsToDouble +import java.lang.Float.intBitsToFloat +import java.util import scala.tools.nsc.io.AbstractFile @@ -25,11 +27,13 @@ import scala.tools.nsc.io.AbstractFile * @author Philippe Altherr * @version 1.0, 23/03/2004 */ -class AbstractFileReader(val file: AbstractFile) { +final class AbstractFileReader(buf0: Array[Byte]) extends DataReader { + private[this] var buf = buf0 - /** the buffer containing the file - */ - val buf: Array[Byte] = file.toByteArray + @deprecated("Use other constructor", "2.13.0") + def this(file: AbstractFile) { + this(file.toByteArray) + } /** the current input pointer */ @@ -62,17 +66,25 @@ class AbstractFileReader(val file: AbstractFile) { ((nextByte & 0xff) << 24) + ((nextByte & 0xff) << 16) + ((nextByte & 0xff) << 8) + (nextByte & 0xff) + /** extract a byte at position bp from buf + */ + def getByte(mybp: Int): Byte = + buf(mybp) + + def getBytes(mybp: Int, bytes: Array[Byte]): Unit = { + System.arraycopy(buf, mybp, bytes, 0, bytes.length) + } /** extract a character at position bp from buf */ def getChar(mybp: Int): Char = - (((buf(mybp) & 0xff) << 8) + (buf(mybp+1) & 0xff)).toChar + (((getByte(mybp) & 0xff) << 8) + (getByte(mybp+1) & 0xff)).toChar /** extract an integer at position bp from buf */ def getInt(mybp: Int): Int = - ((buf(mybp ) & 0xff) << 24) + ((buf(mybp+1) & 0xff) << 16) + - ((buf(mybp+2) & 0xff) << 8) + (buf(mybp+3) & 0xff) + ((getByte(mybp) & 0xff) << 24) + ((getByte(mybp + 1) & 0xff) << 16) + + ((getByte(mybp + 2) & 0xff) << 8) + (getByte(mybp + 3) & 0xff) /** extract a long integer at position bp from buf */ @@ -87,8 +99,11 @@ class AbstractFileReader(val file: AbstractFile) { */ def getDouble(mybp: Int): Double = longBitsToDouble(getLong(mybp)) + def getUTF(mybp: Int, len: Int): String = { + new DataInputStream(new ByteArrayInputStream(buf, mybp, len)).readUTF + } + /** skip next 'n' bytes */ - def skip(n: Int) { bp += n } - + def skip(n: Int): Unit = { bp += n } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala new file mode 100644 index 000000000000..559921ec442c --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala @@ -0,0 +1,89 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc.symtab.classfile + +import java.io.{DataInputStream, InputStream} +import java.nio.{BufferUnderflowException, ByteBuffer} + + +final class ByteBufferDataReader(data0: ByteBuffer) extends DataReader { + private[this] var data = data0 + private[this] val stream = new InputStream { + override def read(): Int = try { + data.get & 0xff + } catch { + case _: BufferUnderflowException => -1 + } + override def markSupported(): Boolean = false + } + private[this] val reader = new DataInputStream(stream) + @throws(classOf[IndexOutOfBoundsException]) + def nextByte: Byte = data.get + + def nextBytes(len: Int): Array[Byte] = { + val result = new Array[Byte](len) + reader.readFully(result) + result + } + + def nextChar: Char = data.getChar() + + def nextInt: Int = data.getInt() + + def getChar(mybp: Int): Char = { + data.getChar(mybp) + } + + def getInt(mybp: Int): Int = { + data.getInt(mybp) + } + + def getLong(mybp: Int): Long = { + data.getLong(mybp) + } + + def getFloat(mybp: Int): Float = { + data.getFloat(mybp) + } + + def getDouble(mybp: Int): Double = { + data.getDouble(mybp) + } + + def skip(n: Int): Unit = { + data.position(data.position() + n) + } + def bp: Int = data.position() + def bp_=(i: Int): Unit = data.position(i) + + def getByte(mybp: Int): Byte = { + data.get(mybp) + } + def getBytes(mybp: Int, bytes: Array[Byte]): Unit = { + val saved = data.position + data.position(mybp) + try reader.readFully(bytes) + finally data.position(saved) + } + def getUTF(mybp: Int, len: Int): String = { + val saved = data.position + val savedLimit = data.limit() + data.position(mybp) + data.limit(mybp + len) + try reader.readUTF() + finally { + data.limit(savedLimit) + data.position(saved) + } + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index d9e43801fcbb..923f3f8b10de 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -17,13 +17,14 @@ package classfile import java.io.{ByteArrayInputStream, DataInputStream, File, IOException} import java.lang.Integer.toHexString +import java.nio.ByteBuffer import scala.collection.{immutable, mutable} import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.annotation.switch import scala.reflect.internal.JavaAccFlags -import scala.reflect.internal.pickling.{ByteCodecs, PickleBuffer} -import scala.reflect.io.NoAbstractFile +import scala.reflect.internal.pickling.ByteCodecs +import scala.reflect.io.{NoAbstractFile, VirtualFile} import scala.reflect.internal.util.Collections._ import scala.tools.nsc.util.ClassPath import scala.tools.nsc.io.AbstractFile @@ -60,8 +61,8 @@ abstract class ClassfileParser { import scala.reflect.internal.ClassfileConstants._ import Flags._ - protected var file: AbstractFile = _ // the class file - protected var in: AbstractFileReader = _ // the class file reader + protected var file: AbstractFile = _ // the class file + protected var in: DataReader = _ // the class file reader protected var clazz: ClassSymbol = _ // the class symbol containing dynamic members protected var staticModule: ModuleSymbol = _ // the module symbol containing static members protected var instanceScope: Scope = _ // the scope of all instance definitions @@ -130,11 +131,6 @@ abstract class ClassfileParser { catch parseErrorHandler finally busy = NoSymbol } - @inline private def raiseLoaderLevel[T](body: => T): T = { - loaders.parentsLevel += 1 - try body - finally loaders.parentsLevel -= 1 - } /** * `clazz` and `module` are the class and module symbols corresponding to the classfile being @@ -148,7 +144,7 @@ abstract class ClassfileParser { def parse(file: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol): Unit = { this.file = file pushBusy(clazz) { - this.in = new AbstractFileReader(file) + this.in = new ByteBufferDataReader(file.toByteBuffer) this.clazz = clazz this.staticModule = module this.isScala = false @@ -162,11 +158,26 @@ abstract class ClassfileParser { private def parseHeader() { val magic = u4 if (magic != JAVA_MAGIC) - abort(s"class file ${in.file} has wrong magic number 0x${toHexString(magic)}") + abort(s"class file ${file} has wrong magic number 0x${toHexString(magic)}") val minor, major = u2 if (major < JAVA_MAJOR_VERSION || major == JAVA_MAJOR_VERSION && minor < JAVA_MINOR_VERSION) - abort(s"class file ${in.file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") + abort(s"class file ${file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") + } + + protected class NameOrString(val value: String) { + private var _name: Name = null + def name: Name = { + if (_name eq null) _name = TermName(value) + _name + } + } + + def getClassSymbol(name: Name): Symbol = { + name match { + case name if nme.isModuleName(name) => rootMirror getModuleByName name.dropModule + case name => classNameToSymbol(name) + } } /** @@ -178,6 +189,8 @@ abstract class ClassfileParser { protected val values = new Array[AnyRef](len) protected val internalized = new Array[NameOrString](len) + val initBp = in.bp + { var i = 1 while (i < starts.length) { starts(i) = in.bp @@ -194,7 +207,7 @@ abstract class ClassfileParser { } } } - + val endBp = in.bp def recordAtIndex[T <: AnyRef](value: T, idx: Int): T = { values(idx) = value value @@ -202,18 +215,11 @@ abstract class ClassfileParser { def firstExpecting(index: Int, expected: Int): Int = { val start = starts(index) - val first = in.buf(start).toInt + val first = in.getByte(start).toInt if (first == expected) start + 1 else this errorBadTag start } - class NameOrString(val value: String) { - private var _name: Name = null - def name: Name = { - if (_name eq null) _name = TermName(value) - _name - } - } /** Return the name found at given index. */ def getName(index: Int): NameOrString = ( if (index <= 0 || len <= index) errorBadIndex(index) @@ -222,13 +228,10 @@ abstract class ClassfileParser { case _ => val start = firstExpecting(index, CONSTANT_UTF8) val len = in.getChar(start).toInt - recordAtIndex(new NameOrString(fromMUTF8(in.buf, start, len + 2)), index) + recordAtIndex(new NameOrString(in.getUTF(start, len + 2)), index) } ) - private def fromMUTF8(bytes: Array[Byte], offset: Int, len: Int): String = - new DataInputStream(new ByteArrayInputStream(bytes, offset, len)).readUTF - /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ def getExternalName(index: Int): NameOrString = { if (index <= 0 || len <= index) @@ -245,10 +248,7 @@ abstract class ClassfileParser { values(index) match { case sym: Symbol => sym case _ => - val result = getClassName(index).name match { - case name if nme.isModuleName(name) => rootMirror getModuleByName name.dropModule - case name => classNameToSymbol(name) - } + val result = ClassfileParser.this.getClassSymbol(getClassName(index).name) recordAtIndex(result, index) } } @@ -258,7 +258,7 @@ abstract class ClassfileParser { */ def getClassName(index: Int): NameOrString = { val start = firstExpecting(index, CONSTANT_CLASS) - getExternalName((in getChar start).toInt) + getExternalName((in.getChar(start)).toInt) } /** Return a name and a type at the given index. If the type is a method @@ -306,11 +306,11 @@ abstract class ClassfileParser { def getType(index: Int): Type = getType(null, index) def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index).value) - def getSuperClass(index: Int): Symbol = if (index == 0) AnyClass else getClassSymbol(index) // the only classfile that is allowed to have `0` in the super_class is java/lang/Object (see jvm spec) + def getSuperClassName(index: Int): NameOrString = if (index == 0) null else getClassName(index) // the only classfile that is allowed to have `0` in the super_class is java/lang/Object (see jvm spec) private def createConstant(index: Int): Constant = { val start = starts(index) - Constant((in.buf(start).toInt: @switch) match { + Constant((in.getByte(start).toInt: @switch) match { case CONSTANT_STRING => getName(in.getChar(start + 1).toInt).value case CONSTANT_INTEGER => in.getInt(start + 1) case CONSTANT_FLOAT => in.getFloat(start + 1) @@ -346,7 +346,7 @@ abstract class ClassfileParser { val start = firstExpecting(index, CONSTANT_UTF8) val len = (in getChar start).toInt val bytes = new Array[Byte](len) - System.arraycopy(in.buf, start + 2, bytes, 0, len) + in.getBytes(start + 2, bytes) recordAtIndex(getSubArray(bytes), index) } ) @@ -360,7 +360,10 @@ abstract class ClassfileParser { if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) val start = firstExpecting(index, CONSTANT_UTF8) val len = (in getChar start).toInt - in.buf drop start + 2 take len + val s = start + 2 + val result = new Array[Byte](len) + in.getBytes(s, result) + result } recordAtIndex(getSubArray(arr), head) } @@ -372,7 +375,7 @@ abstract class ClassfileParser { /** Throws an exception signaling a bad tag at given address. */ protected def errorBadTag(start: Int) = - abort(s"bad constant pool tag ${in.buf(start)} at byte $start") + abort(s"bad constant pool tag ${in.getByte(start)} at byte $start") } def stubClassSymbol(name: Name): Symbol = { @@ -430,9 +433,11 @@ abstract class ClassfileParser { // - was referenced in the bugfix commit for scala/bug#3756 (4fb0d53), not sure why // - covers the case when a type alias in a package object shadows a class symbol, // getClassByName throws a MissingRequirementError (scala-dev#248) - case _: FatalError => + case ex: FatalError => // getClassByName can throw a MissingRequirementError (which extends FatalError) // definitions.getMember can throw a FatalError, for example in pos/t5165b + if (settings.debug) + ex.printStackTrace() stubClassSymbol(name) } @@ -469,6 +474,11 @@ abstract class ClassfileParser { n == "scala.runtime.Nothing$" || n == "scala.runtime.Null$" } + def release(): Unit = { + pool = null + in = null + } + if (isScala) { () // We're done } else if (isScalaRaw && !isNothingOrNull) { @@ -480,26 +490,22 @@ abstract class ClassfileParser { } else { val sflags = jflags.toScalaFlags // includes JAVA - def parseParents(): List[Type] = raiseLoaderLevel { - val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } - else pool.getSuperClass(u2).tpe_* - val ifaceCount = u2 - var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(u2).tpe_* - if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe - superType :: ifaces - } - addEnclosingTParams(clazz) // Create scopes before calling `enterOwnInnerClasses` instanceScope = newScope staticScope = newScope val staticInfo = ClassInfoType(List(), staticScope, moduleClass) - val classInfo = ClassInfoType(parseParents(), instanceScope, clazz) + + val parentIndex = u2 + val parentName = if (parentIndex == 0) null else pool.getClassName(parentIndex) + val ifaceCount = u2 + val ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClassName(u2) + val completer = new ClassTypeCompleter(clazz.name, jflags, parentName, ifaces) enterOwnInnerClasses() - clazz setInfo classInfo + clazz setInfo completer clazz setFlag sflags moduleClass setInfo staticInfo moduleClass setFlag JAVA @@ -512,32 +518,28 @@ abstract class ClassfileParser { skipMembers() // fields skipMembers() // methods - // attributes now depend on having infos set already - parseAttributes(clazz, classInfo) - - def queueLoad() { - in.bp = fieldsStartBp - 0 until u2 foreach (_ => parseField()) - sawPrivateConstructor = false - 0 until u2 foreach (_ => parseMethod()) - val needsConstructor = ( - !sawPrivateConstructor - && !(instanceScope containsName nme.CONSTRUCTOR) - && (sflags & INTERFACE) == 0 - ) - if (needsConstructor) - instanceScope enter clazz.newClassConstructor(NoPosition) - } - - loaders.pendingLoadActions ::= (queueLoad _) - if (loaders.parentsLevel == 0) { - while (loaders.pendingLoadActions.nonEmpty) { - val item = loaders.pendingLoadActions.head - loaders.pendingLoadActions = loaders.pendingLoadActions.tail - item() - } - } + parseAttributes(clazz, completer) + + in.bp = fieldsStartBp + 0 until u2 foreach (_ => parseField()) + sawPrivateConstructor = false + 0 until u2 foreach (_ => parseMethod()) + val needsConstructor = ( + !sawPrivateConstructor + && !(instanceScope containsName nme.CONSTRUCTOR) + && ((sflags & INTERFACE) == 0) + ) + if (needsConstructor) + instanceScope enter clazz.newClassConstructor(NoPosition) + + // we could avoid this if we eagerly created class type param symbols here to expose through the + // ClassTypeCompleter to satisfy the calls to rawInfo.typeParams from Symbol.typeParams. That would + // require a refactor of `sigToType`. + // + // We would also need to make sure that clazzTParams is populated before member type completers called sig2type. + clazz.initialize } + release() } /** Add type parameters of enclosing classes */ @@ -559,17 +561,17 @@ abstract class ClassfileParser { in.skip(4); skipAttributes() } else { val name = readName() - val info = readType() + val lazyInfo = new MemberTypeCompleter(name, jflags, pool.getExternalName(u2).value) val sym = ownerForFlags(jflags).newValue(name.toTermName, NoPosition, sflags) // Note: the info may be overwritten later with a generic signature // parsed from SignatureATTR sym setInfo { if (jflags.isEnum) ConstantType(Constant(sym)) - else info + else lazyInfo } propagatePackageBoundary(jflags, sym) - parseAttributes(sym, info) + parseAttributes(sym, lazyInfo) addJavaFlagsAnnotations(sym, jflags) getScope(jflags) enter sym @@ -594,8 +596,8 @@ abstract class ClassfileParser { val jflags = readMethodFlags() val sflags = jflags.toScalaFlags if (jflags.isPrivate) { - val name = readName() - if (name == nme.CONSTRUCTOR) + val isConstructor = pool.getName(u2).value == "" // opt avoid interning a Name for private methods we're about to discard + if (isConstructor) sawPrivateConstructor = true in.skip(2); skipAttributes() } else { @@ -604,47 +606,13 @@ abstract class ClassfileParser { } else { val name = readName() val sym = ownerForFlags(jflags).newMethod(name.toTermName, NoPosition, sflags) - var info = pool.getType(sym, u2) - var removedOuterParameter = false - if (name == nme.CONSTRUCTOR) - info match { - case MethodType(params, restpe) => - // if this is a non-static inner class, remove the explicit outer parameter - val paramsNoOuter = innerClasses getEntry currentClass match { - case Some(entry) if !entry.jflags.isStatic => - /* About `clazz.owner.hasPackageFlag` below: scala/bug#5957 - * For every nested java class A$B, there are two symbols in the scala compiler. - * 1. created by SymbolLoader, because of the existence of the A$B.class file, owner: package - * 2. created by ClassfileParser of A when reading the inner classes, owner: A - * If symbol 1 gets completed (e.g. because the compiled source mentions `A$B`, not `A#B`), the - * ClassfileParser for 1 executes, and clazz.owner is the package. - */ - assert(params.head.tpe.typeSymbol == clazz.owner || clazz.owner.hasPackageFlag, params.head.tpe.typeSymbol + ": " + clazz.owner) - removedOuterParameter = true - params.tail - case _ => - params - } - val newParams = paramsNoOuter match { - case (init :+ tail) if jflags.isSynthetic => - // scala/bug#7455 strip trailing dummy argument ("access constructor tag") from synthetic constructors which - // are added when an inner class needs to access a private constructor. - init - case _ => - paramsNoOuter - } - - info = MethodType(newParams, clazz.tpe) - } // Note: the info may be overwritten later with a generic signature // parsed from SignatureATTR - sym setInfo info + val lazyInfo = new MemberTypeCompleter(name, jflags, pool.getExternalName(u2).value) + sym.info = lazyInfo propagatePackageBoundary(jflags, sym) - parseAttributes(sym, info, removedOuterParameter) + parseAttributes(sym, lazyInfo) addJavaFlagsAnnotations(sym, jflags) - if (jflags.isVarargs) - sym modifyInfo arrayToRepeated - getScope(jflags) enter sym } } @@ -828,24 +796,16 @@ abstract class ClassfileParser { /** * Only invoked for java classfiles. */ - def parseAttributes(sym: Symbol, symtype: Type, removedOuterParameter: Boolean = false) { - var paramNames: ListBuffer[Name] = null // null means we didn't find any - def convertTo(c: Constant, pt: Type): Constant = { - if (pt.typeSymbol == BooleanClass && c.tag == IntTag) - Constant(c.value != 0) - else - c convertTo pt - } - - def parseAttribute() { + private def parseAttributes(sym: symbolTable.Symbol, completer: JavaTypeCompleter): Unit = { + def parseAttribute(): Unit = { val attrName = readTypeName() val attrLen = u4 attrName match { case tpnme.SignatureATTR => - val sig = pool.getExternalName(u2) - val newType = sigToType(sym, sig.value) - sym.setInfo(newType) - + val sigIndex = u2 + val sig = pool.getExternalName(sigIndex) + assert(sym.rawInfo == completer, sym) + completer.sig = sig.value case tpnme.SyntheticATTR => sym.setFlag(SYNTHETIC | ARTIFACT) in.skip(attrLen) @@ -860,32 +820,20 @@ abstract class ClassfileParser { in.skip(attrLen) case tpnme.ConstantValueATTR => - val c = pool.getConstant(u2) - val c1 = convertTo(c, symtype) - if (c1 ne null) sym.setInfo(ConstantType(c1)) - else devWarning(s"failure to convert $c to $symtype") + completer.constant = pool.getConstant(u2) case tpnme.MethodParametersATTR => def readParamNames(): Unit = { - import scala.tools.asm.Opcodes.ACC_SYNTHETIC val paramCount = u1 + val paramNames = new Array[NameOrString](paramCount) + val paramNameAccess = new Array[Int](paramCount) var i = 0 - if (removedOuterParameter && i < paramCount) { - in.skip(4) - i += 1 - } - paramNames = new ListBuffer() while (i < paramCount) { - val rawname = pool.getName(u2) - val access = u2 - - val name = - if ((access & ACC_SYNTHETIC) == 0) rawname.name.encode - else nme.NO_NAME - - paramNames += name + paramNames(i) = pool.getExternalName(u2) + paramNameAccess(i) = u2 i += 1 } + completer.paramNames = new ParamNames(paramNames, paramNameAccess) } readParamNames() @@ -905,7 +853,7 @@ abstract class ClassfileParser { // i.e. java annotations with RetentionPolicy.CLASS? case tpnme.ExceptionsATTR => - parseExceptions(attrLen) + parseExceptions(attrLen, completer) case tpnme.SourceFileATTR => if (forInteractive) { @@ -926,7 +874,7 @@ abstract class ClassfileParser { case rootMirror.EmptyPackage => srcfileLeaf case pkg => pkg.fullName(File.separatorChar)+File.separator+srcfileLeaf } - srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists) + srcfile0 = settings.outputDirs.srcFilesFor(file, srcpath).find(_.exists) } else in.skip(attrLen) case tpnme.CodeATTR => @@ -945,18 +893,14 @@ abstract class ClassfileParser { * Parse the "Exceptions" attribute which denotes the exceptions * thrown by a method. */ - def parseExceptions(len: Int) { + def parseExceptions(len: Int, completer: JavaTypeCompleter): Unit = { val nClasses = u2 for (n <- 0 until nClasses) { // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (scala/bug#7065) - val cls = pool.getClassSymbol(u2) - // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation - // and that method requires Symbol to be forced to give the right answers, see scala/bug#7107 for details - cls.initialize - sym.addThrowsAnnotation(cls) + val cls = pool.getClassName(u2) + completer.exceptions ::= cls } } - // begin parseAttributes for (i <- 0 until u2) parseAttribute() } @@ -980,7 +924,7 @@ abstract class ClassfileParser { if (s != NoSymbol) Some(LiteralAnnotArg(Constant(s))) else { warning( - sm"""While parsing annotations in ${in.file}, could not find $n in enum ${module.nameString}. + sm"""While parsing annotations in ${file}, could not find $n in enum ${module.nameString}. |This is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (scala/bug#7014).""" ) None @@ -1029,7 +973,7 @@ abstract class ClassfileParser { // the classpath would *not* end up here. A class not found is signaled // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), // and that should never be swallowed silently. - warning(s"Caught: $ex while parsing annotations in ${in.file}") + warning(s"Caught: $ex while parsing annotations in ${file}") if (settings.debug) ex.printStackTrace() None // ignore malformed annotations } @@ -1221,7 +1165,7 @@ abstract class ClassfileParser { AnyRefClass // Force scala.AnyRef, otherwise we get "error: Symbol AnyRef is missing from the classpath" assert(bytes != null, s"No Scala(Long)Signature annotation in classfile with ScalaSignature attribute: $clazz") - unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.name) + unpickler.unpickle(bytes, 0, clazz, staticModule, file.name) } else if (!isScalaRaw && innersStart != -1) { in.bp = innersStart val entries = u2 @@ -1229,17 +1173,17 @@ abstract class ClassfileParser { val innerIndex, outerIndex, nameIndex = u2 val jflags = readInnerClassFlags() if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) - innerClasses add InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) + innerClasses add InnerClassEntry(pool.getClassName(innerIndex), pool.getClassName(outerIndex), pool.getName(nameIndex), jflags) } } in.bp = oldbp } /** An entry in the InnerClasses attribute of this class file. */ - case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: JavaAccFlags) { - def externalName = pool.getClassName(external).name - def outerName = pool.getClassName(outer).name - def originalName = pool.getName(name).name + case class InnerClassEntry(external: NameOrString, outer: NameOrString, name: NameOrString, jflags: JavaAccFlags) { + def externalName = external.name + def outerName = outer.name + def originalName = name.name def isModule = originalName.isTermName def scope = if (jflags.isStatic) staticScope else instanceScope def enclosing = if (jflags.isStatic) enclModule else enclClass @@ -1299,6 +1243,127 @@ abstract class ClassfileParser { sym setInfo createFromClonedSymbols(alias.initialize.typeParams, alias.tpe)(typeFun) } } + private class ParamNames(val names: Array[NameOrString], val access: Array[Int]) { + assert(names.length == access.length) + def length = names.length + } + private abstract class JavaTypeCompleter extends LazyType { + var constant: Constant = _ + var sig: String = _ + var paramNames: ParamNames = _ + var exceptions: List[NameOrString] = Nil + } + private final class ClassTypeCompleter(name: Name, jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter { + override def complete(sym: symbolTable.Symbol): Unit = { + val info = if (sig != null) sigToType(sym, sig) else { + val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } + else getClassSymbol(parent.name).tpe_* + val ifaceCount = u2 + var ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.name).tpe_*) + if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe + ClassInfoType(superType :: ifacesTypes, instanceScope, clazz) + } + sym.setInfo(info) + } + } + + private final class MemberTypeCompleter(name: Name, jflags: JavaAccFlags, descriptor: String) extends JavaTypeCompleter { + override def isJavaVarargsMethod: Boolean = jflags.isVarargs + override def javaThrownExceptions: List[Symbol] = exceptions.map(e => classNameToSymbol(e.name)) + override def complete(sym: symbolTable.Symbol): Unit = { + def descriptorInfo = sigToType(sym, descriptor) + val hasOuterParam = (name == nme.CONSTRUCTOR) && (descriptorInfo match { + case MethodType(params, restpe) => + // if this is a non-static inner class, remove the explicit outer parameter + innerClasses getEntry currentClass match { + case Some(entry) if !entry.jflags.isStatic => + /* About `clazz.owner.hasPackageFlag` below: scala/bug#5957 + * For every nested java class A$B, there are two symbols in the scala compiler. + * 1. created by SymbolLoader, because of the existence of the A$B.class file, owner: package + * 2. created by ClassfileParser of A when reading the inner classes, owner: A + * If symbol 1 gets completed (e.g. because the compiled source mentions `A$B`, not `A#B`), the + * ClassfileParser for 1 executes, and clazz.owner is the package. + */ + assert(params.head.tpe.typeSymbol == clazz.owner || clazz.owner.hasPackageFlag, "" + params.head.tpe.typeSymbol + ": " + clazz.owner) + true + case _ => + false + } + case _ => false + }) + + val info = if (sig != null) { + sigToType(sym, sig) + } else if (name == nme.CONSTRUCTOR) { + descriptorInfo match { + case MethodType(params, restpe) => + val paramsNoOuter = if (hasOuterParam) params.tail else params + val newParams = paramsNoOuter match { + case (init :+ tail) if jflags.isSynthetic => + // scala/bug#7455 strip trailing dummy argument ("access constructor tag") from synthetic constructors which + // are added when an inner class needs to access a private constructor. + init + case _ => + paramsNoOuter + } + MethodType(newParams, clazz.tpe) + case info => info + } + } else { + descriptorInfo + } + if (constant != null) { + val c1 = convertTo(constant, info.resultType) + if (c1 ne null) sym.setInfo(ConstantType(c1)) + else { + devWarning(s"failure to convert $constant to ${info.resultType}") + sym.setInfo(info) + } + } else { + sym.setInfo(if (sym.isMethod && jflags.isVarargs) arrayToRepeated(info) else info) + } + + for (e <- exceptions) { + // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation + // and that method requires Symbol to be forced to give the right answers, see scala/bug#7107 for details + val cls = getClassSymbol(e.name) + sym withAnnotation AnnotationInfo.lazily { + val throwableTpe = cls.tpe_* + AnnotationInfo(appliedType(ThrowsClass, throwableTpe), List(Literal(Constant(throwableTpe))), Nil) + } + } + + // Note: the info may be overwritten later with a generic signature + // parsed from SignatureATTR + if (paramNames != null) { + import scala.tools.asm.Opcodes.ACC_SYNTHETIC + + if (sym.hasRawInfo && sym.isMethod) { + val paramNamesNoOuter = (if (hasOuterParam) 1 else 0) to paramNames.length + val params = sym.rawInfo.params + foreach2(paramNamesNoOuter.toList, params) { + case (i, param) => + val isSynthetic = (paramNames.access(i) & ACC_SYNTHETIC) != 0 + if (!isSynthetic) { + param.name = paramNames.names(i).name.toTermName.encode + param.resetFlag(SYNTHETIC) + } + } + // there's not anything we can do, but it's slightly worrisome + devWarningIf(!sameLength(paramNamesNoOuter.toList, params)) { + sm"""MethodParameters length mismatch while parsing $sym: + | rawInfo.params: ${sym.rawInfo.params}""" + } + } + } + } + private def convertTo(c: Constant, pt: Type): Constant = { + if (pt.typeSymbol == BooleanClass && c.tag == IntTag) + Constant(c.value != 0) + else + c convertTo pt + } + } def skipAttributes() { var attrCount: Int = u2 diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/DataReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/DataReader.scala new file mode 100644 index 000000000000..8c1287ac0df7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/DataReader.scala @@ -0,0 +1,68 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc.symtab.classfile + +trait DataReader { + + def bp: Int + def bp_=(i: Int): Unit + + /** read a byte + */ + @throws(classOf[IndexOutOfBoundsException]) + def nextByte: Byte + + /** read some bytes + */ + def nextBytes(len: Int): Array[Byte] + + /** read a character + */ + def nextChar: Char + + /** read an integer + */ + def nextInt: Int + + /** extract a character at position bp from buf + */ + def getChar(mybp: Int): Char + + /** extract an integer at position bp from buf + */ + def getByte(mybp: Int): Byte + + def getBytes(mybp: Int, bytes: Array[Byte]): Unit + + /** extract an integer at position bp from buf + */ + def getInt(mybp: Int): Int + + /** extract a long integer at position bp from buf + */ + def getLong(mybp: Int): Long + + /** extract a float at position bp from buf + */ + def getFloat(mybp: Int): Float + + /** extract a double at position bp from buf + */ + def getDouble(mybp: Int): Double + + def getUTF(mybp: Int, len: Int): String + + /** skip next 'n' bytes + */ + def skip(n: Int): Unit +} diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 31a54e35f4d1..b471cc465cb8 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -421,7 +421,10 @@ trait Definitions extends api.StandardDefinitions { def isByName(param: Symbol) = isByNameParamType(param.tpe_*) def isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf - def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) + def isJavaVarArgsMethod(m: Symbol) = m.isMethod && (m.rawInfo match { + case completer: LazyType => completer.isJavaVarargsMethod + case _ => isJavaVarArgs(m.info.params) + }) def isJavaVarArgs(params: Seq[Symbol]) = !params.isEmpty && isJavaRepeatedParamType(params.last.tpe) def isScalaVarArgs(params: Seq[Symbol]) = !params.isEmpty && isScalaRepeatedParamType(params.last.tpe) def isVarArgsList(params: Seq[Symbol]) = !params.isEmpty && isRepeatedParamType(params.last.tpe) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d56c5988da85..88e83315e2a3 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3029,7 +3029,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => loop(info) } - override def exceptions = for (ThrownException(tp) <- annotations) yield tp.typeSymbol + override def exceptions = { + rawInfo match { + case lt: LazyType if isJava => + lt.javaThrownExceptions + case _ => + for (ThrownException(tp) <- annotations) yield tp.typeSymbol + } + } } implicit val MethodSymbolTag = ClassTag[MethodSymbol](classOf[MethodSymbol]) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 81e77790e851..6c61b41be09e 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1530,53 +1530,9 @@ trait Types throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) } - object baseClassesCycleMonitor { - private var open: List[Symbol] = Nil - @inline private def cycleLog(msg: => String) { - if (settings.debug) - Console.err.println(msg) - } - def size = open.size - def push(clazz: Symbol) { - cycleLog("+ " + (" " * size) + clazz.fullNameString) - open ::= clazz - } - def pop(clazz: Symbol) { - assert(open.head eq clazz, (clazz, open)) - open = open.tail - } - def isOpen(clazz: Symbol) = open contains clazz - } - protected def defineBaseClassesOfCompoundType(tpe: CompoundType) { - def define() = defineBaseClassesOfCompoundType(tpe, force = false) - if (!breakCycles || isPastTyper) define() - else tpe match { - // non-empty parents helpfully excludes all package classes - case tpe @ ClassInfoType(_ :: _, _, clazz) if !clazz.isAnonOrRefinementClass => - // Cycle: force update - if (baseClassesCycleMonitor isOpen clazz) - defineBaseClassesOfCompoundType(tpe, force = true) - else { - baseClassesCycleMonitor push clazz - try define() - finally baseClassesCycleMonitor pop clazz - } - case _ => - define() - } - } - private def defineBaseClassesOfCompoundType(tpe: CompoundType, force: Boolean) { val period = tpe.baseClassesPeriod - if (period == currentPeriod) { - if (force && breakCycles) { - def what = tpe.typeSymbol + " in " + tpe.typeSymbol.owner.fullNameString - val bcs = computeBaseClasses(tpe) - tpe.baseClassesCache = bcs - warning(s"Breaking cycle in base class computation of $what ($bcs)") - } - } - else { + if (period != currentPeriod) { tpe.baseClassesPeriod = currentPeriod if (!isValidForBaseClasses(period)) { val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, baseClassesNanos) else null @@ -3546,6 +3502,8 @@ trait Types override def complete(sym: Symbol) override def safeToString = "" override def kind = "LazyType" + def isJavaVarargsMethod: Boolean = false + def javaThrownExceptions: List[Symbol] = Nil } /** A marker trait representing an as-yet unevaluated type diff --git a/src/reflect/scala/reflect/io/AbstractFile.scala b/src/reflect/scala/reflect/io/AbstractFile.scala index 714f4f4b5274..b8bd8826a3fc 100644 --- a/src/reflect/scala/reflect/io/AbstractFile.scala +++ b/src/reflect/scala/reflect/io/AbstractFile.scala @@ -17,6 +17,7 @@ package io import java.io.{ IOException, InputStream, OutputStream, BufferedOutputStream, ByteArrayOutputStream } import java.io.{ File => JFile } import java.net.URL +import java.nio.ByteBuffer /** * An abstraction over files for use in the reflection/compiler libraries. @@ -192,6 +193,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { out.toByteArray() } } + def toByteBuffer: ByteBuffer = ByteBuffer.wrap(toByteArray) /** Returns all abstract subfiles of this abstract directory. */ def iterator: Iterator[AbstractFile] diff --git a/src/reflect/scala/reflect/io/PlainFile.scala b/src/reflect/scala/reflect/io/PlainFile.scala index 75ba6e852023..406e6db458a9 100644 --- a/src/reflect/scala/reflect/io/PlainFile.scala +++ b/src/reflect/scala/reflect/io/PlainFile.scala @@ -14,6 +14,10 @@ package scala package reflect package io +import java.nio.ByteBuffer +import java.nio.file.StandardOpenOption +import java.util + /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) { override def isDirectory = true @@ -49,6 +53,20 @@ class PlainFile(val givenPath: Path) extends AbstractFile { override def input = givenPath.toFile.inputStream() override def output = givenPath.toFile.outputStream() override def sizeOption = Some(givenPath.length.toInt) + override def toByteBuffer: ByteBuffer = { + val chan = java.nio.file.Files.newByteChannel(file.toPath, util.EnumSet.of(StandardOpenOption.READ)) + try { + import java.nio.ByteBuffer + val buffer: ByteBuffer = ByteBuffer.allocate(chan.size.toInt) + var endOfInput = false + while (!endOfInput ) { + endOfInput = chan.read(buffer) < 0 + buffer.compact() + } + buffer.flip() + buffer + } finally chan.close() + } override def hashCode(): Int = fpath.hashCode() override def equals(that: Any): Boolean = that match { diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 0b4d7131fbeb..264a3cd9afda 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -162,7 +162,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.SuperType this.TypeBounds this.CompoundType - this.baseClassesCycleMonitor this.RefinedType this.ClassInfoType this.ConstantType diff --git a/test/files/jvm/throws-annot-from-java.check b/test/files/jvm/throws-annot-from-java.check index bf639260e77a..a9974a141e95 100644 --- a/test/files/jvm/throws-annot-from-java.check +++ b/test/files/jvm/throws-annot-from-java.check @@ -11,7 +11,7 @@ scala> :paste val clazz = rootMirror.getClassByName(newTermName("test.ThrowsDeclaration_2")); { val method = clazz.info.member(newTermName("foo")) - val throwsAnn = method.annotations.head + val throwsAnn = method.initialize.annotations.head val atp = throwsAnn.atp println("foo") println("atp.typeParams.isEmpty: " + atp.typeParams.isEmpty) @@ -21,7 +21,7 @@ scala> :paste { val method = clazz.info.member(newTermName("bar")) - val throwsAnn = method.annotations.head + val throwsAnn = method.initialize.annotations.head val Literal(const) = throwsAnn.args.head val tp = const.typeValue println("bar") @@ -37,7 +37,7 @@ atp.typeParams.isEmpty: true throws[IllegalStateException](classOf[java.lang.IllegalStateException]) bar -tp.typeParams.isEmpty: true -throws[test.PolymorphicException[_]](classOf[test.PolymorphicException]) +tp.typeParams.isEmpty: false +throws[test.PolymorphicException](classOf[test.PolymorphicException]) scala> :quit diff --git a/test/files/jvm/throws-annot-from-java/Test_3.scala b/test/files/jvm/throws-annot-from-java/Test_3.scala index de1d9845732a..f2b53bf8ecae 100644 --- a/test/files/jvm/throws-annot-from-java/Test_3.scala +++ b/test/files/jvm/throws-annot-from-java/Test_3.scala @@ -7,7 +7,7 @@ object Test extends ReplTest { val clazz = rootMirror.getClassByName(newTermName("test.ThrowsDeclaration_2")); { val method = clazz.info.member(newTermName("foo")) - val throwsAnn = method.annotations.head + val throwsAnn = method.initialize.annotations.head val atp = throwsAnn.atp println("foo") println("atp.typeParams.isEmpty: " + atp.typeParams.isEmpty) @@ -17,7 +17,7 @@ object Test extends ReplTest { { val method = clazz.info.member(newTermName("bar")) - val throwsAnn = method.annotations.head + val throwsAnn = method.initialize.annotations.head val Literal(const) = throwsAnn.args.head val tp = const.typeValue println("bar") diff --git a/test/files/run/t7008-scala-defined/Impls_Macros_2.scala b/test/files/run/t7008-scala-defined/Impls_Macros_2.scala index 330db8da753b..d49cfff1aa11 100644 --- a/test/files/run/t7008-scala-defined/Impls_Macros_2.scala +++ b/test/files/run/t7008-scala-defined/Impls_Macros_2.scala @@ -5,6 +5,8 @@ object Macros { def impl(c: Context) = { import c.universe._ val decls = c.typeOf[ScalaClassWithCheckedExceptions_1[_]].decls.toList + decls.foreach(_.info) + decls.foreach(_.annotations.foreach(_.tpe)) val s = decls.sortBy(_.name.toString).map(decl => (s"${decl.name}: ${decl.annotations}")).mkString(scala.compat.Platform.EOL) reify(println(c.Expr[String](Literal(Constant(s))).splice)) } diff --git a/test/files/run/t7008/Impls_Macros_2.scala b/test/files/run/t7008/Impls_Macros_2.scala index 3c6fe116ce2e..e55cbbfdbf8d 100644 --- a/test/files/run/t7008/Impls_Macros_2.scala +++ b/test/files/run/t7008/Impls_Macros_2.scala @@ -5,6 +5,8 @@ object Macros { def impl(c: Context) = { import c.universe._ val decls = c.typeOf[JavaClassWithCheckedExceptions_1[_]].decls.toList + decls.foreach(_.info) + decls.foreach(_.annotations.foreach(_.tpe)) val s = decls.sortBy(_.name.toString).map(decl => (s"${decl.name}: ${decl.annotations}")).mkString(scala.compat.Platform.EOL) reify(println(c.Expr[String](Literal(Constant(s))).splice)) } diff --git a/test/files/run/t7455/Test.scala b/test/files/run/t7455/Test.scala index 2cda9225f4fa..afe3f09fb57e 100644 --- a/test/files/run/t7455/Test.scala +++ b/test/files/run/t7455/Test.scala @@ -23,8 +23,8 @@ object Test extends DirectTest { clazz = compiler.rootMirror.staticClass(name) constr <- clazz.info.member(termNames.CONSTRUCTOR).alternatives } { - println(constr.defString) fullyInitializeSymbol(constr) + println(constr.defString) } } } From d9fbd48ddc654001fed8df0ad42fecba267941aa Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Mar 2019 15:02:00 +1000 Subject: [PATCH 05/12] Reuse the buffer for classfile reading Classfile parsing does re-enter when we're reading package objects or classfiles for things like `scala/native.class`. But for the most part the prior refactorings mean that we typically only parse a single classfile at a time, and as such we can profit from a one-element cache for the buffer to read this into. (cherry picked from commit ed8d95eb3092a6fd239820362034b42ad636d85b) --- .../tools/nsc/symtab/SymbolLoaders.scala | 8 +- .../classfile/ByteBufferDataReader.scala | 89 ----------- .../symtab/classfile/ClassfileParser.scala | 29 ++-- .../symtab/classfile/ReusableDataReader.scala | 149 ++++++++++++++++++ .../scala/reflect/internal/Symbols.scala | 2 +- 5 files changed, 168 insertions(+), 109 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala create mode 100644 src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 8dbb110ce5ea..847b1837bbe7 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -13,13 +13,13 @@ package scala.tools.nsc package symtab -import classfile.ClassfileParser +import classfile.{ClassfileParser, ReusableDataReader} import java.io.IOException import scala.reflect.internal.MissingRequirementError import scala.reflect.io.{AbstractFile, NoAbstractFile} import scala.tools.nsc.util.{ClassPath, ClassRepresentation} import scala.reflect.internal.TypesStats -import scala.reflect.internal.util.StatisticsStatics +import scala.reflect.internal.util.{ReusableInstance, StatisticsStatics} /** This class ... * @@ -301,11 +301,11 @@ abstract class SymbolLoaders { } } } - + private val classFileDataReader: ReusableInstance[ReusableDataReader] = new ReusableInstance[ReusableDataReader](() => new ReusableDataReader()) class ClassfileLoader(val classfile: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol) extends SymbolLoader with FlagAssigningCompleter { private object classfileParser extends { val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable - } with ClassfileParser { + } with ClassfileParser(classFileDataReader) { override protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = SymbolLoaders.this.lookupMemberAtTyperPhaseIfPossible(sym, name) /* diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala deleted file mode 100644 index 559921ec442c..000000000000 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Scala (https://www.scala-lang.org) - * - * Copyright EPFL and Lightbend, Inc. - * - * Licensed under Apache License 2.0 - * (http://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package scala.tools.nsc.symtab.classfile - -import java.io.{DataInputStream, InputStream} -import java.nio.{BufferUnderflowException, ByteBuffer} - - -final class ByteBufferDataReader(data0: ByteBuffer) extends DataReader { - private[this] var data = data0 - private[this] val stream = new InputStream { - override def read(): Int = try { - data.get & 0xff - } catch { - case _: BufferUnderflowException => -1 - } - override def markSupported(): Boolean = false - } - private[this] val reader = new DataInputStream(stream) - @throws(classOf[IndexOutOfBoundsException]) - def nextByte: Byte = data.get - - def nextBytes(len: Int): Array[Byte] = { - val result = new Array[Byte](len) - reader.readFully(result) - result - } - - def nextChar: Char = data.getChar() - - def nextInt: Int = data.getInt() - - def getChar(mybp: Int): Char = { - data.getChar(mybp) - } - - def getInt(mybp: Int): Int = { - data.getInt(mybp) - } - - def getLong(mybp: Int): Long = { - data.getLong(mybp) - } - - def getFloat(mybp: Int): Float = { - data.getFloat(mybp) - } - - def getDouble(mybp: Int): Double = { - data.getDouble(mybp) - } - - def skip(n: Int): Unit = { - data.position(data.position() + n) - } - def bp: Int = data.position() - def bp_=(i: Int): Unit = data.position(i) - - def getByte(mybp: Int): Byte = { - data.get(mybp) - } - def getBytes(mybp: Int, bytes: Array[Byte]): Unit = { - val saved = data.position - data.position(mybp) - try reader.readFully(bytes) - finally data.position(saved) - } - def getUTF(mybp: Int, len: Int): String = { - val saved = data.position - val savedLimit = data.limit() - data.position(mybp) - data.limit(mybp + len) - try reader.readUTF() - finally { - data.limit(savedLimit) - data.position(saved) - } - } -} diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 923f3f8b10de..4ce7313f8951 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -24,6 +24,7 @@ import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.annotation.switch import scala.reflect.internal.JavaAccFlags import scala.reflect.internal.pickling.ByteCodecs +import scala.reflect.internal.util.ReusableInstance import scala.reflect.io.{NoAbstractFile, VirtualFile} import scala.reflect.internal.util.Collections._ import scala.tools.nsc.util.ClassPath @@ -35,7 +36,7 @@ import scala.util.control.NonFatal * @author Martin Odersky * @version 1.0 */ -abstract class ClassfileParser { +abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { val symbolTable: SymbolTable { def settings: Settings } @@ -144,14 +145,18 @@ abstract class ClassfileParser { def parse(file: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol): Unit = { this.file = file pushBusy(clazz) { - this.in = new ByteBufferDataReader(file.toByteBuffer) - this.clazz = clazz - this.staticModule = module - this.isScala = false - - parseHeader() - this.pool = new ConstantPool - parseClass() + reader.using { reader => + this.in = reader.reset(file) + this.clazz = clazz + this.staticModule = module + this.isScala = false + + parseHeader() + this.pool = new ConstantPool + parseClass() + pool = null + in = null + } } } @@ -474,11 +479,6 @@ abstract class ClassfileParser { n == "scala.runtime.Nothing$" || n == "scala.runtime.Null$" } - def release(): Unit = { - pool = null - in = null - } - if (isScala) { () // We're done } else if (isScalaRaw && !isNothingOrNull) { @@ -539,7 +539,6 @@ abstract class ClassfileParser { // We would also need to make sure that clazzTParams is populated before member type completers called sig2type. clazz.initialize } - release() } /** Add type parameters of enclosing classes */ diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala new file mode 100644 index 000000000000..8236fca02c24 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala @@ -0,0 +1,149 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc.symtab.classfile + +import java.io.{ByteArrayInputStream, DataInputStream, InputStream} +import java.nio.channels.Channels +import java.nio.{BufferUnderflowException, ByteBuffer} + +final class ReusableDataReader() extends DataReader { + private[this] var data = new Array[Byte](32768) + private[this] var bb: ByteBuffer = ByteBuffer.wrap(data) + private[this] var size = 0 + private[this] val reader: DataInputStream = { + val stream = new InputStream { + override def read(): Int = try { + bb.get & 0xff + } catch { + case _: BufferUnderflowException => -1 + } + override def markSupported(): Boolean = false + } + new DataInputStream(stream) + } + + private def nextPositivePowerOfTwo(target: Int): Int = 1 << -Integer.numberOfLeadingZeros(target - 1) + + def reset(file: scala.reflect.io.AbstractFile): this.type = { + this.size = 0 + file.sizeOption match { + case Some(size) => + if (size > data.length) { + data = new Array[Byte](nextPositivePowerOfTwo(size)) + } else { + java.util.Arrays.fill(data, 0.toByte) + } + val input = file.input + try { + var endOfInput = false + while (!endOfInput) { + val remaining = data.length - this.size + if (remaining == 0) endOfInput = true + else { + val read = input.read(data, this.size, remaining) + if (read < 0) endOfInput = true + else this.size += read + } + } + bb = ByteBuffer.wrap(data, 0, size) + } finally { + input.close() + } + case None => + val input = file.input + try { + var endOfInput = false + while (!endOfInput) { + val remaining = data.length - size + if (remaining == 0) { + data = java.util.Arrays.copyOf(data, nextPositivePowerOfTwo(size)) + } + val read = input.read(data, this.size, data.length - this.size) + if (read < 0) endOfInput = true + else this.size += read + } + bb = ByteBuffer.wrap(data, 0, size) + } finally { + input.close() + } + } + this + } + + @throws(classOf[IndexOutOfBoundsException]) + def nextByte: Byte = bb.get + + def nextBytes(len: Int): Array[Byte] = { + val result = new Array[Byte](len) + reader.readFully(result) + result + } + + def nextChar: Char = bb.getChar() + + def nextInt: Int = bb.getInt() + + def getChar(mybp: Int): Char = { + bb.getChar(mybp) + } + + def getInt(mybp: Int): Int = { + bb.getInt(mybp) + } + + def getLong(mybp: Int): Long = { + bb.getLong(mybp) + } + + def getFloat(mybp: Int): Float = { + bb.getFloat(mybp) + } + + def getDouble(mybp: Int): Double = { + bb.getDouble(mybp) + } + + def skip(n: Int): Unit = { + bb.position(bb.position() + n) + } + def bp: Int = bb.position() + def bp_=(i: Int): Unit = { + try { + bb.position(i) + } catch { + case ex: IllegalArgumentException => + throw ex + } + } + + def getByte(mybp: Int): Byte = { + bb.get(mybp) + } + def getBytes(mybp: Int, bytes: Array[Byte]): Unit = { + val saved = bb.position() + bb.position(mybp) + try reader.readFully(bytes) + finally bb.position(saved) + } + def getUTF(mybp: Int, len: Int): String = { + val saved = bb.position() + val savedLimit = bb.limit() + bb.position(mybp) + bb.limit(mybp + len) + try reader.readUTF() + finally { + bb.limit(savedLimit) + bb.position(saved) + } + } +} diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 88e83315e2a3..fc6172f3e8e6 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -24,7 +24,7 @@ import scala.collection.mutable.ListBuffer import util.{ Statistics, shortClassOfInstance, StatisticsStatics } import Flags._ import scala.annotation.tailrec -import scala.reflect.io.{ AbstractFile, NoAbstractFile } +import scala.reflect.io.{AbstractFile, NoAbstractFile} import Variance._ trait Symbols extends api.Symbols { self: SymbolTable => From 0a064805435c17529a13059667f459beb096c784 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Mar 2019 15:03:37 +1000 Subject: [PATCH 06/12] Avoid using Names for fully qualified class names There is no good reason for these dotted names to be Names and stick around in the name table. Let's use short lived strings instead. Reduces the name table by 5% in terms of entries and 10% in terms of characters when compiling src/scalap/**/*.scala (cherry picked from commit ae18049a6c5f8851e01ac5baebb4b95262df0685) --- src/compiler/scala/tools/nsc/Global.scala | 3 + .../symtab/classfile/ClassfileParser.scala | 82 ++++++++++--------- .../scala/reflect/internal/Definitions.scala | 20 ++--- .../scala/reflect/internal/Mirrors.scala | 73 +++++++++++------ .../scala/reflect/internal/Names.scala | 2 + .../scala/reflect/internal/StdNames.scala | 26 +++--- test/files/jvm/throws-annot-from-java.check | 2 +- .../jvm/throws-annot-from-java/Test_3.scala | 2 +- test/files/run/compiler-asSeenFrom.scala | 2 +- test/files/run/existentials-in-compiler.scala | 4 +- test/files/run/t7096.scala | 2 +- 11 files changed, 122 insertions(+), 96 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 93fd46d01887..55c14fc5e9c7 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1542,6 +1542,9 @@ class Global(var currentSettings: Settings, reporter0: Reporter) reporting.summarizeErrors() + // val allNamesArray: Array[String] = allNames().map(_.toString).toArray.sorted + // allNamesArray.foreach(println(_)) + if (traceSymbolActivity) units map (_.body) foreach (traceSymbols recordSymbolsInTree _) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 4ce7313f8951..708369558fc1 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -72,7 +72,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { protected var isScala: Boolean = _ // does class file describe a scala class? protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info protected var busy: Symbol = _ // lock to detect recursive reads - protected var currentClass: Name = _ // JVM name of the current class + protected var currentClass: String = _ // JVM name of the current class protected var classTParams = Map[Name,Symbol]() protected var srcfile0 : Option[AbstractFile] = None protected def moduleClass: Symbol = staticModule.moduleClass @@ -178,9 +178,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } } - def getClassSymbol(name: Name): Symbol = { + def getClassSymbol(name: String): Symbol = { name match { - case name if nme.isModuleName(name) => rootMirror getModuleByName name.dropModule + case name if name.endsWith(nme.MODULE_SUFFIX_STRING) => rootMirror getModuleByName newTermName(name).dropModule case name => classNameToSymbol(name) } } @@ -253,7 +253,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { values(index) match { case sym: Symbol => sym case _ => - val result = ClassfileParser.this.getClassSymbol(getClassName(index).name) + val result = ClassfileParser.this.getClassSymbol(getClassName(index).value) recordAtIndex(result, index) } } @@ -304,7 +304,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { val name = getClassName(index) name.value.charAt(0) match { case ARRAY_TAG => recordAtIndex(sigToType(null, name.value), index) - case _ => recordAtIndex(classNameToSymbol(name.name), index).tpe_* + case _ => recordAtIndex(classNameToSymbol(name.value), index).tpe_* } } } @@ -396,13 +396,13 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { NoSymbol.newStubSymbol(name.toTypeName, msg) } - private def lookupClass(name: Name) = try { + private def lookupClass(name: String) = try { def lookupTopLevel = { - if (name containsChar '.') + if (name contains '.') rootMirror getClassByName name else // FIXME - we shouldn't be doing ad hoc lookups in the empty package, getClassByName should return the class - definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName) + definitions.getMember(rootMirror.EmptyPackageClass, newTypeName(name)) } // For inner classes we usually don't get here: `classNameToSymbol` already returns the symbol @@ -413,21 +413,23 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { // what the logic below is for (see PR #5822 / scala/bug#9937). val split = if (isScalaRaw) -1 else name.lastIndexOf('$') if (split > 0 && split < name.length) { - val outerName = name.subName(0, split) - val innerName = name.subName(split + 1, name.length).toTypeName + val outerName = name.substring(0, split) + val innerName = name.substring(split + 1, name.length) val outerSym = classNameToSymbol(outerName) // If the outer class C cannot be found, look for a top-level class C$D if (outerSym.isInstanceOf[StubSymbol]) lookupTopLevel else { + val innerNameAsName = newTypeName(innerName) + // We have a java-defined class name C$D and look for a member D of C. But we don't know if // D is declared static or not, so we have to search both in class C and its companion. val r = if (outerSym == clazz) - staticScope.lookup(innerName) orElse - instanceScope.lookup(innerName) + staticScope.lookup(innerNameAsName) orElse + instanceScope.lookup(innerNameAsName) else - lookupMemberAtTyperPhaseIfPossible(outerSym, innerName) orElse - lookupMemberAtTyperPhaseIfPossible(outerSym.companionModule, innerName) + lookupMemberAtTyperPhaseIfPossible(outerSym, innerNameAsName) orElse + lookupMemberAtTyperPhaseIfPossible(outerSym.companionModule, innerNameAsName) r orElse lookupTopLevel } } else @@ -443,11 +445,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { // definitions.getMember can throw a FatalError, for example in pos/t5165b if (settings.debug) ex.printStackTrace() - stubClassSymbol(name) + stubClassSymbol(newTypeName(name)) } /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name): Symbol = { + def classNameToSymbol(name: String): Symbol = { if (innerClasses contains name) innerClasses innerSymbol name else @@ -459,10 +461,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { val jflags = readClassFlags() val classNameIndex = u2 - currentClass = pool.getClassName(classNameIndex).name + currentClass = pool.getClassName(classNameIndex).value // Ensure that (top-level) classfiles are in the correct directory - val isTopLevel = !(currentClass containsChar '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532) + val isTopLevel = !(currentClass contains '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532) if (isTopLevel) { val c = pool.getClassSymbol(classNameIndex) // scala-dev#248: when a type alias (in a package object) shadows a class symbol, getClassSymbol returns a stub @@ -625,10 +627,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { assert(sig.charAt(index) == ch, (sig.charAt(index), ch)) index += 1 } - def subName(isDelimiter: Char => Boolean): Name = { + def subName(isDelimiter: Char => Boolean): String = { val start = index while (!isDelimiter(sig.charAt(index))) { index += 1 } - newTermName(sigChars, start, index - start) + new String(sigChars, start, index - start) } def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = { val tag = sig.charAt(index); index += 1 @@ -700,7 +702,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { var tpe = processClassType(processInner(classSym.tpe_*)) while (sig.charAt(index) == '.') { accept('.') - val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName + val name = newTypeName(subName(c => c == ';' || c == '<' || c == '.')) val clazz = tpe.member(name) val dummyArgs = Nil // the actual arguments are added in processClassType val inner = typeRef(pre = tpe, sym = clazz, args = dummyArgs) @@ -737,7 +739,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { sig2type(tparams, skiptvs) JavaMethodType(sym.newSyntheticValueParams(paramtypes.toList), restype) case 'T' => - val n = subName(';'.==).toTypeName + val n = newTypeName(subName(';'.==)) index += 1 if (skiptvs) AnyTpe else tparams(n).typeConstructor @@ -761,7 +763,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { index += 1 val start = index while (sig.charAt(index) != '>') { - val tpname = subName(':'.==).toTypeName + val tpname = newTypeName(subName(':'.==)) val s = sym.newTypeParameter(tpname) tparams = tparams + (tpname -> s) sig2typeBounds(tparams, skiptvs = true) @@ -769,7 +771,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } index = start while (sig.charAt(index) != '>') { - val tpname = subName(':'.==).toTypeName + val tpname = newTypeName(subName(':'.==)) val s = tparams(tpname) s.setInfo(sig2typeBounds(tparams, skiptvs = false)) } @@ -986,9 +988,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { /** Enter own inner classes in the right scope. It needs the scopes to be set up, * and implicitly current class' superclasses. */ - private def enterOwnInnerClasses() { - def className(name: Name): Name = - name.subName(name.lastPos('.') + 1, name.length) + private def enterOwnInnerClasses(): Unit = { + def className(name: String): String = + name.substring(name.lastIndexOf('.') + 1, name.length) def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile) { def jflags = entry.jflags @@ -1036,8 +1038,8 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { decls unlink e } - val cName = className(entry.externalName) - unlinkIfPresent(cName.toTermName) + val cName = newTermName(className(entry.externalName)) + unlinkIfPresent(cName) unlinkIfPresent(cName.toTypeName) } @@ -1180,15 +1182,15 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { /** An entry in the InnerClasses attribute of this class file. */ case class InnerClassEntry(external: NameOrString, outer: NameOrString, name: NameOrString, jflags: JavaAccFlags) { - def externalName = external.name - def outerName = outer.name + def externalName = external.value + def outerName = outer.value def originalName = name.name def isModule = originalName.isTermName def scope = if (jflags.isStatic) staticScope else instanceScope def enclosing = if (jflags.isStatic) enclModule else enclClass // The name of the outer class, without its trailing $ if it has one. - private def strippedOuter = outerName.dropModule + private def strippedOuter = outerName.stripSuffix(nme.MODULE_SUFFIX_STRING) private def isInner = innerClasses contains strippedOuter private def enclClass = if (isInner) innerClasses innerSymbol strippedOuter else classNameToSymbol(strippedOuter) private def enclModule = enclClass.companionModule @@ -1200,10 +1202,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { * If the given name is not an inner class, it returns the symbol found in `definitions`. */ object innerClasses { - private val inners = mutable.HashMap[Name, InnerClassEntry]() + private val inners = mutable.HashMap[String, InnerClassEntry]() - def contains(name: Name) = inners contains name - def getEntry(name: Name) = inners get name + def contains(name: String) = inners contains name + def getEntry(name: String) = inners get name def entries = inners.values def add(entry: InnerClassEntry): Unit = { @@ -1213,7 +1215,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } inners(entry.externalName) = entry } - def innerSymbol(externalName: Name): Symbol = this getEntry externalName match { + def innerSymbol(externalName: String): Symbol = this getEntry externalName match { case Some(entry) => innerSymbol(entry) case _ => NoSymbol } @@ -1256,9 +1258,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { override def complete(sym: symbolTable.Symbol): Unit = { val info = if (sig != null) sigToType(sym, sig) else { val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } - else getClassSymbol(parent.name).tpe_* + else getClassSymbol(parent.value).tpe_* val ifaceCount = u2 - var ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.name).tpe_*) + var ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.value).tpe_*) if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe ClassInfoType(superType :: ifacesTypes, instanceScope, clazz) } @@ -1268,7 +1270,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { private final class MemberTypeCompleter(name: Name, jflags: JavaAccFlags, descriptor: String) extends JavaTypeCompleter { override def isJavaVarargsMethod: Boolean = jflags.isVarargs - override def javaThrownExceptions: List[Symbol] = exceptions.map(e => classNameToSymbol(e.name)) + override def javaThrownExceptions: List[Symbol] = exceptions.map(e => classNameToSymbol(e.value)) override def complete(sym: symbolTable.Symbol): Unit = { def descriptorInfo = sigToType(sym, descriptor) val hasOuterParam = (name == nme.CONSTRUCTOR) && (descriptorInfo match { @@ -1325,7 +1327,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { for (e <- exceptions) { // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation // and that method requires Symbol to be forced to give the right answers, see scala/bug#7107 for details - val cls = getClassSymbol(e.name) + val cls = getClassSymbol(e.value) sym withAnnotation AnnotationInfo.lazily { val throwableTpe = cls.tpe_* AnnotationInfo(appliedType(ThrowsClass, throwableTpe), List(Literal(Constant(throwableTpe))), Nil) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index b471cc465cb8..a119ea0cdab1 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -191,11 +191,11 @@ trait Definitions extends api.StandardDefinitions { // It becomes tricky to create dedicated objects for other symbols because // of initialization order issues. - lazy val JavaLangPackage = getPackage(TermName("java.lang")) + lazy val JavaLangPackage = getPackage("java.lang") lazy val JavaLangPackageClass = JavaLangPackage.moduleClass.asClass - lazy val ScalaPackage = getPackage(TermName("scala")) + lazy val ScalaPackage = getPackage("scala") lazy val ScalaPackageClass = ScalaPackage.moduleClass.asClass - lazy val RuntimePackage = getPackage(TermName("scala.runtime")) + lazy val RuntimePackage = getPackage("scala.runtime") lazy val RuntimePackageClass = RuntimePackage.moduleClass.asClass def javaTypeToValueClass(jtype: Class[_]): Symbol = jtype match { @@ -291,7 +291,7 @@ trait Definitions extends api.StandardDefinitions { // top types lazy val AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) markAllCompleted lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe) markAllCompleted - lazy val ObjectClass = getRequiredClass(sn.Object.toString) + lazy val ObjectClass = getRequiredClass("java.lang.Object") // Cached types for core monomorphic classes lazy val AnyRefTpe = AnyRefClass.tpe @@ -342,12 +342,12 @@ trait Definitions extends api.StandardDefinitions { // exceptions and other throwables lazy val ClassCastExceptionClass = requiredClass[ClassCastException] - lazy val IndexOutOfBoundsExceptionClass = getClassByName(sn.IOOBException) - lazy val InvocationTargetExceptionClass = getClassByName(sn.InvTargetException) + lazy val IndexOutOfBoundsExceptionClass = getClassByName("java.lang.IndexOutOfBoundsException") + lazy val InvocationTargetExceptionClass = getClassByName("java.lang.reflect.InvocationTargetException") lazy val MatchErrorClass = requiredClass[MatchError] lazy val NonLocalReturnControlClass = requiredClass[scala.runtime.NonLocalReturnControl[_]] - lazy val NullPointerExceptionClass = getClassByName(sn.NPException) - lazy val ThrowableClass = getClassByName(sn.Throwable) + lazy val NullPointerExceptionClass = getClassByName("java.lang.NullPointerException") + lazy val ThrowableClass = getClassByName("java.lang.Throwable") lazy val UninitializedErrorClass = requiredClass[UninitializedFieldError] lazy val IllegalArgExceptionClass = requiredClass[IllegalArgumentException] @@ -490,7 +490,7 @@ trait Definitions extends api.StandardDefinitions { // reflection / structural types lazy val SoftReferenceClass = requiredClass[java.lang.ref.SoftReference[_]] - lazy val MethodClass = getClassByName(sn.MethodAsObject) + lazy val MethodClass = getClassByName("java.lang.reflect.Method") lazy val EmptyMethodCacheClass = requiredClass[scala.runtime.EmptyMethodCache] lazy val MethodCacheClass = requiredClass[scala.runtime.MethodCache] def methodCache_find = getMemberMethod(MethodCacheClass, nme.find_) @@ -1221,7 +1221,7 @@ trait Definitions extends api.StandardDefinitions { // Trying to allow for deprecated locations sym.isAliasType && isMetaAnnotation(sym.info.typeSymbol) ) - lazy val metaAnnotations: Set[Symbol] = getPackage(TermName("scala.annotation.meta")).info.members filter (_ isSubClass StaticAnnotationClass) toSet + lazy val metaAnnotations: Set[Symbol] = getPackage("scala.annotation.meta").info.members filter (_ isSubClass StaticAnnotationClass) toSet // According to the scala.annotation.meta package object: // * By default, annotations on (`val`-, `var`- or plain) constructor parameters diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index befaa49175a1..0ca0794600a9 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -46,19 +46,23 @@ trait Mirrors extends api.Mirrors { } /** Todo: organize similar to mkStatic in scala.reflect.Base */ - private def getModuleOrClass(path: Name, len: Int): Symbol = { - val point = path lastPos('.', len - 1) + private def getModuleOrClass(path: Name, len: Int): Symbol = + getModuleOrClass(path.toString, len, path.newName(_)) + + private def getModuleOrClass(path: String, len: Int, toName: String => Name): Symbol = { + val point = path lastIndexOf ('.', len - 1) val owner = - if (point > 0) getModuleOrClass(path.toTermName, point) + if (point > 0) getModuleOrClass(path, point, newTermName(_)) else RootClass - val name = path subName (point + 1, len) + + val name = toName(path.substring(point + 1, len)) val sym = owner.info member name - val result = if (path.isTermName) sym.suchThat(_ hasFlag MODULE) else sym + val result = if (name.isTermName) sym.suchThat(_ hasFlag MODULE) else sym if (result != NoSymbol) result else { if (settings.debug) { log(sym.info); log(sym.info.members) }//debug thisMirror.missingHook(owner, name) orElse { - MissingRequirementError.notFound((if (path.isTermName) "object " else "class ")+path+" in "+thisMirror) + MissingRequirementError.notFound((if (name.isTermName) "object " else "class ")+path+" in "+thisMirror) } } } @@ -69,8 +73,8 @@ trait Mirrors extends api.Mirrors { * Unlike `getModuleOrClass`, this function * loads unqualified names from the root package. */ - private def getModuleOrClass(path: Name): Symbol = - getModuleOrClass(path, path.length) + private def getModuleOrClass(path: String, toName: String => Name): Symbol = + getModuleOrClass(path, path.length, toName) /** If you're looking for a class, pass a type name. * If a module, a term name. @@ -78,10 +82,10 @@ trait Mirrors extends api.Mirrors { * Unlike `getModuleOrClass`, this function * loads unqualified names from the empty package. */ - private def staticModuleOrClass(path: Name): Symbol = { - val isPackageless = path.pos('.') == path.length - if (isPackageless) EmptyPackageClass.info decl path - else getModuleOrClass(path) + private def staticModuleOrClass(path: String, toName: String => Name): Symbol = { + val isPackageless = !path.contains('.') + if (isPackageless) EmptyPackageClass.info decl toName(path) + else getModuleOrClass(path, toName) } protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = NoSymbol @@ -104,28 +108,41 @@ trait Mirrors extends api.Mirrors { } } + @deprecated("Use overload that accepts a String.", "2.13.0") def getClassByName(fullname: Name): ClassSymbol = - ensureClassSymbol(fullname.toString, getModuleOrClass(fullname.toTypeName)) + ensureClassSymbol(fullname.toString, getModuleOrClass(fullname.toString, fullname.length, newTypeName(_))) + + def getClassByName(fullname: String): ClassSymbol = + getRequiredClass(fullname) + + // TODO_NAMES + def getRequiredClass(fullname: String, toName: String => Name): ClassSymbol = + ensureClassSymbol(fullname, getModuleOrClass(fullname, fullname.length, toName)) def getRequiredClass(fullname: String): ClassSymbol = - getClassByName(newTypeNameCached(fullname)) + ensureClassSymbol(fullname, getModuleOrClass(fullname, fullname.length, newTypeName(_))) def requiredClass[T: ClassTag] : ClassSymbol = - getRequiredClass(erasureName[T]) + getRequiredClass(erasureName[T], newTypeName(_)) def getClassIfDefined(fullname: String): Symbol = - getClassIfDefined(newTypeNameCached(fullname)) + getClassIfDefined(fullname, newTypeName(_)) + @deprecated("Use overload that accepts a String.", "2.13.0") def getClassIfDefined(fullname: Name): Symbol = wrapMissing(getClassByName(fullname.toTypeName)) + // TODO_NAMES + def getClassIfDefined(fullname: String, toName: String => Name): Symbol = + wrapMissing(getRequiredClass(fullname, toName)) + /** @inheritdoc * * Unlike getClassByName/getRequiredClass this function can also load packageless symbols. * Compiler might ignore them, but they should be loadable with macros. */ override def staticClass(fullname: String): ClassSymbol = - try ensureClassSymbol(fullname, staticModuleOrClass(newTypeNameCached(fullname))) + try ensureClassSymbol(fullname, staticModuleOrClass(fullname, newTypeName(_))) catch { case mre: MissingRequirementError => throw new ScalaReflectionException(mre.msg) } /************************ loaders of module symbols ************************/ @@ -136,11 +153,15 @@ trait Mirrors extends api.Mirrors { case _ => MissingRequirementError.notFound("object " + fullname) } + @deprecated("Use overload that accepts a String.", "2.13.0") def getModuleByName(fullname: Name): ModuleSymbol = - ensureModuleSymbol(fullname.toString, getModuleOrClass(fullname.toTermName), allowPackages = true) + getModuleByName(fullname.toString) + + def getModuleByName(fullname: String): ModuleSymbol = + ensureModuleSymbol(fullname, getModuleOrClass(fullname, fullname.length, newTermName(_)), allowPackages = true) def getRequiredModule(fullname: String): ModuleSymbol = - getModuleByName(newTermNameCached(fullname)) + getModuleByName(fullname) // TODO: What syntax do we think should work here? Say you have an object // like scala.Predef. You can't say requiredModule[scala.Predef] since there's @@ -153,10 +174,11 @@ trait Mirrors extends api.Mirrors { getRequiredModule(erasureName[T] stripSuffix "$") def getModuleIfDefined(fullname: String): Symbol = - getModuleIfDefined(newTermNameCached(fullname)) + wrapMissing(getModuleByName(fullname)) + @deprecated("Use overload that accepts a String.", "2.13.0") def getModuleIfDefined(fullname: Name): Symbol = - wrapMissing(getModuleByName(fullname.toTermName)) + getModuleIfDefined(fullname.toString) /** @inheritdoc * @@ -164,7 +186,7 @@ trait Mirrors extends api.Mirrors { * Compiler might ignore them, but they should be loadable with macros. */ override def staticModule(fullname: String): ModuleSymbol = - try ensureModuleSymbol(fullname, staticModuleOrClass(newTermNameCached(fullname)), allowPackages = false) + try ensureModuleSymbol(fullname, staticModuleOrClass(fullname, newTermName(_)), allowPackages = false) catch { case mre: MissingRequirementError => throw new ScalaReflectionException(mre.msg) } /************************ loaders of package symbols ************************/ @@ -175,8 +197,11 @@ trait Mirrors extends api.Mirrors { case _ => MissingRequirementError.notFound("package " + fullname) } + @deprecated("Use overload that accepts a String.", "2.13.0") def getPackage(fullname: TermName): ModuleSymbol = - ensurePackageSymbol(fullname.toString, getModuleOrClass(fullname), allowModules = true) + getPackage(fullname.toString) + def getPackage(fullname: String): ModuleSymbol = + ensurePackageSymbol(fullname, getModuleOrClass(fullname, newTermName(_)), allowModules = true) def getPackageIfDefined(fullname: TermName): Symbol = wrapMissing(getPackage(fullname)) @@ -198,7 +223,7 @@ trait Mirrors extends api.Mirrors { wrapMissing(getPackageObject(fullname)) override def staticPackage(fullname: String): ModuleSymbol = - try ensurePackageSymbol(fullname.toString, getModuleOrClass(newTermNameCached(fullname)), allowModules = false) + try ensurePackageSymbol(fullname.toString, getModuleOrClass(fullname, fullname.length, newTermName(_)), allowModules = false) catch { case mre: MissingRequirementError => throw new ScalaReflectionException(mre.msg) } /************************ helpers ************************/ diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index fc6596a52c3f..ed48c7af7d2c 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -47,6 +47,8 @@ trait Names extends api.Names { /** Hashtable for finding type names quickly. */ private val typeHashtable = new Array[TypeName](HASH_SIZE) + final def allNames(): Iterator[TermName] = termHashtable.iterator.filter(_ ne null).flatMap(n => Iterator.iterate(n)(_.next).takeWhile(_ ne null)) + /** * The hashcode of a name depends on the first, the last and the middle character, * and the length of the name. diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 38b64f63dc56..47b87432c7a5 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -1166,21 +1166,15 @@ trait StdNames { protected val stringToTypeName = null protected implicit def createNameType(s: String): TypeName = newTypeNameCached(s) - final val BoxedBoolean: TypeName = "java.lang.Boolean" - final val BoxedByte: TypeName = "java.lang.Byte" - final val BoxedCharacter: TypeName = "java.lang.Character" - final val BoxedDouble: TypeName = "java.lang.Double" - final val BoxedFloat: TypeName = "java.lang.Float" - final val BoxedInteger: TypeName = "java.lang.Integer" - final val BoxedLong: TypeName = "java.lang.Long" - final val BoxedNumber: TypeName = "java.lang.Number" - final val BoxedShort: TypeName = "java.lang.Short" - final val IOOBException: TypeName = "java.lang.IndexOutOfBoundsException" - final val InvTargetException: TypeName = "java.lang.reflect.InvocationTargetException" - final val MethodAsObject: TypeName = "java.lang.reflect.Method" - final val NPException: TypeName = "java.lang.NullPointerException" - final val Object: TypeName = "java.lang.Object" - final val Throwable: TypeName = "java.lang.Throwable" + final val BoxedBoolean: String = "java.lang.Boolean" + final val BoxedByte: String = "java.lang.Byte" + final val BoxedCharacter: String = "java.lang.Character" + final val BoxedDouble: String = "java.lang.Double" + final val BoxedFloat: String = "java.lang.Float" + final val BoxedInteger: String = "java.lang.Integer" + final val BoxedLong: String = "java.lang.Long" + final val BoxedNumber: String = "java.lang.Number" + final val BoxedShort: String = "java.lang.Short" final val GetCause: TermName = newTermName("getCause") final val GetClass: TermName = newTermName("getClass") @@ -1193,7 +1187,7 @@ trait StdNames { final val AltMetafactory: TermName = newTermName("altMetafactory") final val Bootstrap: TermName = newTermName("bootstrap") - val Boxed = immutable.Map[TypeName, TypeName]( + val Boxed = immutable.Map[TypeName, String]( tpnme.Boolean -> BoxedBoolean, tpnme.Byte -> BoxedByte, tpnme.Char -> BoxedCharacter, diff --git a/test/files/jvm/throws-annot-from-java.check b/test/files/jvm/throws-annot-from-java.check index a9974a141e95..4a4bd6ad2110 100644 --- a/test/files/jvm/throws-annot-from-java.check +++ b/test/files/jvm/throws-annot-from-java.check @@ -8,7 +8,7 @@ scala> :paste // Entering paste mode (ctrl-D to finish) { - val clazz = rootMirror.getClassByName(newTermName("test.ThrowsDeclaration_2")); + val clazz = rootMirror.getClassByName("test.ThrowsDeclaration_2"); { val method = clazz.info.member(newTermName("foo")) val throwsAnn = method.initialize.annotations.head diff --git a/test/files/jvm/throws-annot-from-java/Test_3.scala b/test/files/jvm/throws-annot-from-java/Test_3.scala index f2b53bf8ecae..df62e032262e 100644 --- a/test/files/jvm/throws-annot-from-java/Test_3.scala +++ b/test/files/jvm/throws-annot-from-java/Test_3.scala @@ -4,7 +4,7 @@ object Test extends ReplTest { def code = """:power :paste { - val clazz = rootMirror.getClassByName(newTermName("test.ThrowsDeclaration_2")); + val clazz = rootMirror.getClassByName("test.ThrowsDeclaration_2"); { val method = clazz.info.member(newTermName("foo")) val throwsAnn = method.initialize.annotations.head diff --git a/test/files/run/compiler-asSeenFrom.scala b/test/files/run/compiler-asSeenFrom.scala index a60c2e892524..940907665820 100644 --- a/test/files/run/compiler-asSeenFrom.scala +++ b/test/files/run/compiler-asSeenFrom.scala @@ -42,7 +42,7 @@ abstract class CompilerTest extends DirectTest { } class SymsInPackage(pkgName: String) { - def pkg = rootMirror.getPackage(TermName(pkgName)) + def pkg = rootMirror.getPackage(pkgName) def classes = allMembers(pkg) filter (_.isClass) def modules = allMembers(pkg) filter (_.isModule) def symbols = classes ++ terms filterNot (_ eq NoSymbol) diff --git a/test/files/run/existentials-in-compiler.scala b/test/files/run/existentials-in-compiler.scala index e35b7231c2d3..2984d81e6009 100644 --- a/test/files/run/existentials-in-compiler.scala +++ b/test/files/run/existentials-in-compiler.scala @@ -74,8 +74,8 @@ package extest { } """ - override def check(source: String, unit: global.CompilationUnit) { - getPackage(TermName("extest")).moduleClass.info.decls.toList.filter(_.isType).map(_.initialize).sortBy(_.name.toString) foreach { clazz => + override def check(source: String, unit: global.CompilationUnit): Unit = { + getPackage("extest").moduleClass.info.decls.toList.filter(_.isType).map(_.initialize).sortBy(_.name.toString) foreach { clazz => exitingTyper { clazz.info println(clazz.defString) diff --git a/test/files/run/t7096.scala b/test/files/run/t7096.scala index f723d70abef8..44485e5da1e8 100644 --- a/test/files/run/t7096.scala +++ b/test/files/run/t7096.scala @@ -41,7 +41,7 @@ abstract class CompilerTest extends DirectTest { } class SymsInPackage(pkgName: String) { - def pkg = rootMirror.getPackage(TermName(pkgName)) + def pkg = rootMirror.getPackage(pkgName) def classes = allMembers(pkg) filter (_.isClass) def modules = allMembers(pkg) filter (_.isModule) def symbols = classes ++ terms filterNot (_ eq NoSymbol) From afb50a88ddd34b8c2c1e0aaee3a0fc6d4d771bf9 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Mar 2019 15:07:48 +1000 Subject: [PATCH 07/12] fixup --- project/VersionUtil.scala | 20 +++++++++++++++++-- .../symtab/classfile/ClassfileParser.scala | 2 +- .../symtab/classfile/ReusableDataReader.scala | 7 +++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index dd8e18dd8c16..5060ed6fa8ef 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -5,6 +5,9 @@ import Keys._ import java.util.{Date, Locale, Properties, TimeZone} import java.io.{File, FileInputStream} import java.text.SimpleDateFormat +import java.time.Instant +import java.time.format.DateTimeFormatter +import java.time.temporal.{TemporalAccessor, TemporalQueries, TemporalQuery} import scala.collection.JavaConverters._ import BuildSettings.autoImport._ @@ -71,8 +74,21 @@ object VersionUtil { val db = new FileRepositoryBuilder().findGitDir.build val head = db.resolve("HEAD") if (head eq null) { - log.info("No git HEAD commit found -- Using current date and 'unknown' SHA") - (new Date, "unknown") + import scala.sys.process._ + try { + // Workaround lack of git worktree support in JGit https://bugs.eclipse.org/bugs/show_bug.cgi?id=477475 + val sha = List("git", "rev-parse", "HEAD").!!.trim + val commitDateIso = List("git", "log", "-1", "--format=%cI", "HEAD").!!.trim + val date = java.util.Date.from(DateTimeFormatter.ISO_DATE_TIME.parse(commitDateIso, new TemporalQuery[Instant] { + override def queryFrom(temporal: TemporalAccessor): Instant = Instant.from(temporal) + })) + (date, sha.substring(0, 7)) + } catch { + case ex: Exception => + ex.printStackTrace() + log.info("No git HEAD commit found -- Using current date and 'unknown' SHA") + (new Date, "unknown") + } } else { val commit = new RevWalk(db).parseCommit(head) (new Date(commit.getCommitTime.toLong * 1000L), commit.getName.substring(0, 7)) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 708369558fc1..ce1a3195defb 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1261,7 +1261,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { else getClassSymbol(parent.value).tpe_* val ifaceCount = u2 var ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.value).tpe_*) - if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe + if (jflags.isAnnotation) ifacesTypes ::= ClassfileAnnotationClass.tpe ClassInfoType(superType :: ifacesTypes, instanceScope, clazz) } sym.setInfo(info) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala index 8236fca02c24..8bbbc4a3cce6 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ReusableDataReader.scala @@ -27,6 +27,13 @@ final class ReusableDataReader() extends DataReader { } catch { case _: BufferUnderflowException => -1 } + + override def read(b: Array[Byte], off: Int, len: Int): Int = { + val pos = bb.position() + bb.get(b, off, len) + bb.position() - pos + } + override def markSupported(): Boolean = false } new DataInputStream(stream) From f559f4be60cfbbf94b63ec93844c4ab9230be630 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Mar 2019 15:20:17 +1000 Subject: [PATCH 08/12] fixup --- .../scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index ce1a3195defb..98ba03796e20 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1257,9 +1257,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { private final class ClassTypeCompleter(name: Name, jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter { override def complete(sym: symbolTable.Symbol): Unit = { val info = if (sig != null) sigToType(sym, sig) else { - val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } - else getClassSymbol(parent.value).tpe_* - val ifaceCount = u2 + val superType = + if (parent == null) AnyClass.tpe_* + else if (jflags.isAnnotation) { u2; AnnotationClass.tpe } + else getClassSymbol(parent.value).tpe_* var ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.value).tpe_*) if (jflags.isAnnotation) ifacesTypes ::= ClassfileAnnotationClass.tpe ClassInfoType(superType :: ifacesTypes, instanceScope, clazz) From fc5132843fc8dc8cc96eef11d2de7ff1db737e58 Mon Sep 17 00:00:00 2001 From: Harrison Houghton Date: Tue, 4 Jun 2019 17:16:13 -0400 Subject: [PATCH 09/12] Also deprecate backing field symbols. Compiling @deprecated val foo: T = some.deprecated(call) yielded private[this] val `foo `: T = some.deprecated(call) @deprecated def foo: T = this.`foo ` where the `@deprecated` has been slapped on the def (where it'll incur deprecation warnings on callers) but not on the val (where it'll suppress deprecation warnings on the body. Just copy the annotation across. Fixes scala/bug#11538 in an expedient manner. --- src/library/scala/deprecated.scala | 2 +- test/files/pos/t11538.flags | 1 + test/files/pos/t11538.scala | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t11538.flags create mode 100644 test/files/pos/t11538.scala diff --git a/src/library/scala/deprecated.scala b/src/library/scala/deprecated.scala index b35288a22915..42dccf60cb69 100644 --- a/src/library/scala/deprecated.scala +++ b/src/library/scala/deprecated.scala @@ -64,5 +64,5 @@ import scala.annotation.meta._ * @see [[scala.deprecatedOverriding]] * @see [[scala.deprecatedName]] */ -@getter @setter @beanGetter @beanSetter +@getter @setter @beanGetter @beanSetter @field class deprecated(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation diff --git a/test/files/pos/t11538.flags b/test/files/pos/t11538.flags new file mode 100644 index 000000000000..7882ee62698f --- /dev/null +++ b/test/files/pos/t11538.flags @@ -0,0 +1 @@ +-Xfatal-warnings -deprecation -stop:refchecks \ No newline at end of file diff --git a/test/files/pos/t11538.scala b/test/files/pos/t11538.scala new file mode 100644 index 000000000000..77c931e2c202 --- /dev/null +++ b/test/files/pos/t11538.scala @@ -0,0 +1,13 @@ +package t11538 + +@deprecated("not for you", since = "just now") +class Abhorrent + +object Bizzle { + @deprecated("use mipple instead", since = "recently") + val wibble: Abhorrent = mipple + @deprecated("use wobble instead", since = "recently") + def mipple: Abhorrent = wobble + @deprecated("use wibble instead", since = "recently") + var wobble: Abhorrent = wibble +} \ No newline at end of file From 4627c4e0031526bdd3a151f096c958e485217f62 Mon Sep 17 00:00:00 2001 From: "ta.tanaka" Date: Fri, 7 Jun 2019 01:02:37 +0900 Subject: [PATCH 10/12] Awaitable.result should have a throws annotation of TimeoutException and InterruptedException as well as Awaitable.ready. --- src/library/scala/concurrent/Awaitable.scala | 3 ++- src/library/scala/concurrent/Future.scala | 3 ++- src/library/scala/concurrent/package.scala | 3 ++- test/files/jvm/future-spec/main.scala | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala index 4714b351944b..d201a14570f2 100644 --- a/src/library/scala/concurrent/Awaitable.scala +++ b/src/library/scala/concurrent/Awaitable.scala @@ -60,7 +60,8 @@ trait Awaitable[+T] { * @throws TimeoutException if after waiting for the specified time this `Awaitable` is still not ready * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]] */ - @throws(classOf[Exception]) + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) def result(atMost: Duration)(implicit permit: CanAwait): T } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 8f6983b27d1d..4f12a8379419 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -578,7 +578,8 @@ object Future { throw new TimeoutException(s"Future timed out after [$atMost]") } - @throws(classOf[Exception]) + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) override def result(atMost: Duration)(implicit permit: CanAwait): Nothing = { ready(atMost) throw new TimeoutException(s"Future timed out after [$atMost]") diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 042b1ab636d0..bc3853a0b98b 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -214,7 +214,8 @@ package concurrent { * @throws TimeoutException if after waiting for the specified time `awaitable` is still not ready * @throws IllegalArgumentException if `atMost` is [[scala.concurrent.duration.Duration.Undefined Duration.Undefined]] */ - @throws(classOf[Exception]) + @throws(classOf[TimeoutException]) + @throws(classOf[InterruptedException]) def result[T](awaitable: Awaitable[T], atMost: Duration): T = blocking(awaitable.result(atMost)(AwaitPermission)) } diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index 697d0fe91f3f..f5db78e30b11 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -107,7 +107,7 @@ class TestLatch(count: Int = 1) extends Awaitable[Unit] { this } - @throws(classOf[Exception]) + @throws(classOf[TimeoutException]) def result(atMost: Duration)(implicit permit: CanAwait): Unit = { ready(atMost) } From a3fdd73783b82bb94e5c01f74a6773bc7ccc4b53 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 16 Jun 2019 16:29:43 +1000 Subject: [PATCH 11/12] Windows compat for PipelineMainTest, finally? https://stackoverflow.com/questions/39628328/trying-to-create-a-directory-immediately-after-a-successful-deleteifexists-throw --- test/junit/scala/tools/nsc/PipelineMainTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/junit/scala/tools/nsc/PipelineMainTest.scala b/test/junit/scala/tools/nsc/PipelineMainTest.scala index 8d4218029c6d..e779cfc774e7 100644 --- a/test/junit/scala/tools/nsc/PipelineMainTest.scala +++ b/test/junit/scala/tools/nsc/PipelineMainTest.scala @@ -263,8 +263,8 @@ class PipelineMainTest { class CleanVisitor() extends SimpleFileVisitor[Path] { override def preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult = { if (dir.getFileName.toString == "target") { - deleteRecursive(dir) - Files.createDirectories(dir) + for (p <- Files.list(dir).iterator.asScala) + deleteRecursive(p) FileVisitResult.SKIP_SUBTREE } else super.preVisitDirectory(dir, attrs) } From 205f1c532d0a1b54a2b1874db4c4a553284911b6 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 17 Jun 2019 07:25:06 +1000 Subject: [PATCH 12/12] Close .args file after reading --- src/compiler/scala/tools/nsc/CompilerCommand.scala | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 66eb574d97d8..86f9e0aa6c1e 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -12,6 +12,8 @@ package scala.tools.nsc +import java.nio.file.Files + import io.File /** A class representing command line info for scalac */ @@ -119,11 +121,12 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { */ def expandArg(arg: String): List[String] = { def stripComment(s: String) = s takeWhile (_ != '#') - val file = File(arg stripPrefix "@") - if (!file.exists) - throw new java.io.FileNotFoundException("argument file %s could not be found" format file.name) - - settings splitParams (file.lines() map stripComment mkString " ") + import java.nio.file._ + import collection.JavaConverters._ + val file = Paths.get(arg stripPrefix "@") + if (!Files.exists(file)) + throw new java.io.FileNotFoundException("argument file %s could not be found" format file) + settings splitParams (Files.readAllLines(file).asScala map stripComment mkString " ") } // override this if you don't want arguments processed here