diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 18ccced75ebc..9c7b74c29155 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -19,7 +19,7 @@ import scala.tools.nsc.io.AbstractFile * @version 1.0 * */ -abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { +abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters with GenCommon { import global._ @@ -694,17 +694,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { } } - /* Whether an annotation should be emitted as a Java annotation - * .initialize: if 'annot' is read from pickle, atp might be un-initialized - * - * must-single-thread - */ - private def shouldEmitAnnotation(annot: AnnotationInfo) = - annot.symbol.initialize.isJavaDefined && - annot.matches(definitions.ClassfileAnnotationClass) && - annot.args.isEmpty && - !annot.matches(definitions.DeprecatedAttr) - /* * In general, * must-single-thread @@ -724,7 +713,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = cw.visitAnnotation(descriptor(typ), true) + val av = cw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -736,7 +725,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = mw.visitAnnotation(descriptor(typ), true) + val av = mw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -748,7 +737,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = fw.visitAnnotation(descriptor(typ), true) + val av = fw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -763,7 +752,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { annot <- annots) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), true) + val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), isRuntimeVisible(annot)) emitAssocs(pannVisitor, assocs) } } @@ -859,13 +848,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCForwardersGen extends BCAnnotGen with BCJGenSigGen { - // ----------------------------------------------------------------------------------------- - // Static forwarders (related to mirror classes but also present in - // a plain class lacking companion module, for details see `isCandidateForForwarders`). - // ----------------------------------------------------------------------------------------- - - val ExcludedForwarderFlags = genASM.ExcludedForwarderFlags - /* Adds a @remote annotation, actual use unknown. * * Invoked from genMethod() and addForwarder(). diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index eb40e1dbdeb3..61f1b89cd934 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -20,7 +20,7 @@ import scala.annotation.tailrec * * Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf */ -abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { +abstract class GenASM extends SubComponent with BytecodeWriters with GenCommon { import global._ import icodes._ import icodes.opcodes._ @@ -96,6 +96,63 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } } + private def isJavaEntryPoint(icls: IClass) = { + val sym = icls.symbol + def fail(msg: String, pos: Position = sym.pos) = { + icls.cunit.warning(sym.pos, + sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + + " Reason: " + msg + // TODO: make this next claim true, if possible + // by generating valid main methods as static in module classes + // not sure what the jvm allows here + // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." + ) + false + } + def failNoForwarder(msg: String) = { + fail(msg + ", which means no static forwarder can be generated.\n") + } + val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil + val hasApproximate = possibles exists { m => + m.info match { + case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass + case _ => false + } + } + // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. + hasApproximate && { + // Before erasure so we can identify generic mains. + enteringErasure { + val companion = sym.linkedClassOfClass + + if (hasJavaMainMethod(companion)) + failNoForwarder("companion contains its own main method") + else if (companion.tpe.member(nme.main) != NoSymbol) + // this is only because forwarders aren't smart enough yet + failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") + else if (companion.isTrait) + failNoForwarder("companion is a trait") + // Now either succeeed, or issue some additional warnings for things which look like + // attempts to be java main methods. + else (possibles exists isJavaMainMethod) || { + possibles exists { m => + m.info match { + case PolyType(_, _) => + fail("main methods cannot be generic.") + case MethodType(params, res) => + if (res.typeSymbol :: params exists (_.isAbstractType)) + fail("main methods cannot refer to type parameters or abstract types.", m.pos) + else + isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) + case tp => + fail("don't know what this is: " + tp, m.pos) + } + } + } + } + } + } + override def run() { if (settings.debug) @@ -794,15 +851,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { for (ThrownException(exc) <- excs.distinct) yield javaName(exc) - /** Whether an annotation should be emitted as a Java annotation - * .initialize: if 'annot' is read from pickle, atp might be un-initialized - */ - private def shouldEmitAnnotation(annot: AnnotationInfo) = - annot.symbol.initialize.isJavaDefined && - annot.matches(ClassfileAnnotationClass) && - annot.args.isEmpty && - !annot.matches(DeprecatedAttr) - // @M don't generate java generics sigs for (members of) implementation // classes, as they are monomorphic (TODO: ok?) private def needsGenericSignature(sym: Symbol) = !( @@ -989,7 +1037,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = cw.visitAnnotation(descriptor(typ), true) + val av = cw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -998,7 +1046,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = mw.visitAnnotation(descriptor(typ), true) + val av = mw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -1007,7 +1055,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = fw.visitAnnotation(descriptor(typ), true) + val av = fw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -1019,7 +1067,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { annot <- annots) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), true) + val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), isRuntimeVisible(annot)) emitAssocs(pannVisitor, assocs) } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenCommon.scala new file mode 100644 index 000000000000..a0684af04892 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenCommon.scala @@ -0,0 +1,48 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Jason Zaugg + */ + +package scala.tools.nsc +package backend.jvm +import scala.tools.nsc.symtab._ + +trait GenCommon extends BytecodeWriters { + import global._ + import definitions.{ AnnotationRetentionAttr, AnnotationRetentionPolicyAttr, ClassfileAnnotationClass } + + // ----------------------------------------------------------------------------------------- + // Static forwarders (related to mirror classes but also present in + // a plain class lacking companion module, for details see `isCandidateForForwarders`). + // ----------------------------------------------------------------------------------------- + val ExcludedForwarderFlags = { + import Flags._ + // Should include DEFERRED but this breaks findMember. + (SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO) + } + + lazy val AnnotationRetentionPolicyModule = AnnotationRetentionPolicyAttr.companionModule + lazy val AnnotationRetentionPolicySourceValue = AnnotationRetentionPolicyModule.tpe.member(TermName("SOURCE")) + lazy val AnnotationRetentionPolicyClassValue = AnnotationRetentionPolicyModule.tpe.member(TermName("CLASS")) + lazy val AnnotationRetentionPolicyRuntimeValue = AnnotationRetentionPolicyModule.tpe.member(TermName("RUNTIME")) + + /** + * Whether an annotation should be emitted as a Java annotation + * .initialize: if 'annot' is read from pickle, atp might be un-initialized + */ + def shouldEmitAnnotation(annot: AnnotationInfo) = + annot.symbol.initialize.isJavaDefined && + annot.matches(ClassfileAnnotationClass) && + retentionPolicyOf(annot) != AnnotationRetentionPolicySourceValue && + annot.args.isEmpty + + def isRuntimeVisible(annot: AnnotationInfo): Boolean = + annot.atp.getAnnotation(AnnotationRetentionAttr) + .exists(_.assocs.contains((nme.value -> LiteralAnnotArg(Constant(AnnotationRetentionPolicyRuntimeValue))))) + + def retentionPolicyOf(annot: AnnotationInfo): Symbol = + annot.atp.getAnnotation(AnnotationRetentionAttr).map(_.assocs).map(assoc => + assoc.collectFirst { + case (`nme`.value, LiteralAnnotArg(Constant(value: Symbol))) => value + }.getOrElse(AnnotationRetentionPolicyClassValue)).getOrElse(AnnotationRetentionPolicyClassValue) +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala deleted file mode 100644 index 01c4ff5a52b7..000000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala +++ /dev/null @@ -1,83 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Jason Zaugg - */ - -package scala.tools.nsc -package backend.jvm -import scala.tools.nsc.symtab._ - -/** Code shared between the erstwhile legacy backend (aka GenJVM) - * and the new backend [[scala.tools.nsc.backend.jvm.GenASM]]. There should be - * more here, but for now I'm starting with the refactorings that are either - * straightforward to review or necessary for maintenance. - */ -trait GenJVMASM { - val global: Global - import global._ - import icodes._ - import definitions._ - - val ExcludedForwarderFlags = { - import Flags._ - // Should include DEFERRED but this breaks findMember. - ( SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO ) - } - - protected def isJavaEntryPoint(icls: IClass) = { - val sym = icls.symbol - def fail(msg: String, pos: Position = sym.pos) = { - icls.cunit.warning(sym.pos, - sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + - " Reason: " + msg - // TODO: make this next claim true, if possible - // by generating valid main methods as static in module classes - // not sure what the jvm allows here - // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." - ) - false - } - def failNoForwarder(msg: String) = { - fail(msg + ", which means no static forwarder can be generated.\n") - } - val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil - val hasApproximate = possibles exists { m => - m.info match { - case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass - case _ => false - } - } - // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. - hasApproximate && { - // Before erasure so we can identify generic mains. - enteringErasure { - val companion = sym.linkedClassOfClass - - if (hasJavaMainMethod(companion)) - failNoForwarder("companion contains its own main method") - else if (companion.tpe.member(nme.main) != NoSymbol) - // this is only because forwarders aren't smart enough yet - failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") - else if (companion.isTrait) - failNoForwarder("companion is a trait") - // Now either succeeed, or issue some additional warnings for things which look like - // attempts to be java main methods. - else (possibles exists isJavaMainMethod) || { - possibles exists { m => - m.info match { - case PolyType(_, _) => - fail("main methods cannot be generic.") - case MethodType(params, res) => - if (res.typeSymbol :: params exists (_.isAbstractType)) - fail("main methods cannot refer to type parameters or abstract types.", m.pos) - else - isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) - case tp => - fail("don't know what this is: " + tp, m.pos) - } - } - } - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 8b739958ff0a..f5223472dffe 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -206,7 +206,7 @@ abstract class SymbolLoaders { override def complete(root: Symbol) { try { - val start = currentTime + val start = System.nanoTime() val currentphase = phase doComplete(root) phase = currentphase diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7a0c70caf682..911c35a727a5 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -1058,6 +1058,10 @@ trait Definitions extends api.StandardDefinitions { lazy val ClassfileAnnotationClass = requiredClass[scala.annotation.ClassfileAnnotation] lazy val StaticAnnotationClass = requiredClass[scala.annotation.StaticAnnotation] + // Java retention annotations + lazy val AnnotationRetentionAttr = requiredClass[java.lang.annotation.Retention] + lazy val AnnotationRetentionPolicyAttr = requiredClass[java.lang.annotation.RetentionPolicy] + // Annotations lazy val BridgeClass = requiredClass[scala.annotation.bridge] lazy val ElidableMethodClass = requiredClass[scala.annotation.elidable] diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 571c4cfa5d61..724c49520671 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -56,7 +56,7 @@ abstract class SymbolTable extends macros.Universe def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) protected def elapsedMessage(msg: String, start: Long) = - msg + " in " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start) + "ms" + msg + " in " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "ms" def informProgress(msg: String) = if (settings.verbose) inform("[" + msg + "]") def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index fb893cbff145..e1bb678e5f09 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -384,6 +384,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.AnnotationClass definitions.ClassfileAnnotationClass definitions.StaticAnnotationClass + definitions.AnnotationRetentionAttr + definitions.AnnotationRetentionPolicyAttr definitions.BridgeClass definitions.ElidableMethodClass definitions.ImplicitNotFoundClass diff --git a/test/files/run/t4788.check b/test/files/run/t4788.check new file mode 100644 index 000000000000..3ddc86c6d34a --- /dev/null +++ b/test/files/run/t4788.check @@ -0,0 +1,4 @@ +warning: there were 1 deprecation warning(s); re-run with -deprecation for details +None +Some(@Ljavax/jws/soap/InitParam;(name="", value="") // invisible) +Some(@Ljavax/xml/ws/Action;()) diff --git a/test/files/run/t4788/C.scala b/test/files/run/t4788/C.scala new file mode 100644 index 000000000000..5484ac4ea6d7 --- /dev/null +++ b/test/files/run/t4788/C.scala @@ -0,0 +1,2 @@ +@javax.jws.soap.InitParam(name="", value="") // RetentionPolicy.CLASS +class C diff --git a/test/files/run/t4788/R.scala b/test/files/run/t4788/R.scala new file mode 100644 index 000000000000..f0c20df138bc --- /dev/null +++ b/test/files/run/t4788/R.scala @@ -0,0 +1,2 @@ +@javax.xml.ws.Action // RetentionPolicy.RUNTIME +class R diff --git a/test/files/run/t4788/S.scala b/test/files/run/t4788/S.scala new file mode 100644 index 000000000000..b55b55dd383a --- /dev/null +++ b/test/files/run/t4788/S.scala @@ -0,0 +1,2 @@ +@javax.annotation.Generated(Array("")) // RetentionPolicy.SOURCE +class S diff --git a/test/files/run/t4788/Test.scala b/test/files/run/t4788/Test.scala new file mode 100644 index 000000000000..63ae94ade202 --- /dev/null +++ b/test/files/run/t4788/Test.scala @@ -0,0 +1,22 @@ +import java.io.PrintWriter; + +import scala.tools.partest.BytecodeTest +import scala.tools.asm.util._ +import scala.tools.nsc.util.stringFromWriter + +object Test extends BytecodeTest { + def annotationsForClass(className: String): Option[String] = { + val classNode = loadClassNode(className, skipDebugInfo = false) + val textifier = new Textifier + classNode.accept(new TraceClassVisitor(null, textifier, null)) + + val classString = stringFromWriter(w => textifier.print(w)) + classString.split('\n').find(_.contains("@Ljava")).map(_.trim) + } + + def show { + println(annotationsForClass("S")) + println(annotationsForClass("C")) + println(annotationsForClass("R")) + } +}