diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 36a72abe911a..0fbae89aec28 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -201,7 +201,8 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter val plainC = pcb.cnode if (claszSymbol.isClass) // @DarkDimius is this test needed here? - for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) { + for (pickler <- ctx.compilationUnit.picklers.get(claszSymbol.asClass)) { + val binary = pickler.assembleParts() val dataAttr = new CustomAttr(nme.TASTYATTR.mangledString, binary) val store = if (mirrorC ne null) mirrorC else plainC store.visitAttribute(dataAttr) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index da8b3923be1e..16a59250bb7f 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -17,6 +17,12 @@ class CompilationUnit(val source: SourceFile) { def isJava = source.file.name.endsWith(".java") - /** Pickled TASTY binaries, indexed by class. */ - var pickled: Map[ClassSymbol, Array[Byte]] = Map() + /** + * Picklers used to create TASTY sections, indexed by toplevel class to which they belong. + * Sections: Header, ASTs and Positions are populated by `pickler` phase. + * Subsequent phases can add new sections. + */ + var picklers: Map[ClassSymbol, TastyPickler] = Map() + + var unpicklers: Map[ClassSymbol, TastyUnpickler] = Map() } diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index ea77e0e50192..299448a398ae 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -13,6 +13,7 @@ import Phases.Phase import transform._ import util.FreshNameCreator import transform.TreeTransforms.{TreeTransform, TreeTransformer} +import transform.linker._ import core.DenotTransformers.DenotTransformer import core.Denotations.SingleDenotation @@ -47,6 +48,8 @@ class Compiler { List(new PostTyper), // Additional checks and cleanups after type checking List(new sbt.ExtractAPI), // Sends a representation of the API of classes to sbt via callbacks List(new Pickler), // Generate TASTY info + List(new CollectSummaries), // Collects method summaries for the call graph construction + List(new BuildCallGraph), // Builds the call graph List(new FirstTransform, // Some transformations to put trees into a canonical form new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars new ElimJavaPackages), // Eliminate syntactic references to Java packages @@ -69,6 +72,8 @@ class Compiler { new ShortcutImplicits, // Allow implicit functions without creating closures new CrossCastAnd, // Normalize selections involving intersection types. new Splitter), // Expand selections involving union types into conditionals + List(new OuterSpecializer), + List(new OuterSpecializeParents), List(new VCInlineMethods, // Inlines calls to value class methods new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions new SeqLiterals, // Express vararg arguments as arrays @@ -82,7 +87,7 @@ class Compiler { new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. - List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types + List(new ElimErasedValueType, // Expand erased value types to their underlying implementation types new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations new Mixin, // Expand trait fields and trait initializers new LazyVals, // Expand lazy vals @@ -94,6 +99,8 @@ class Compiler { // Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions. new GetClass, // Rewrites getClass calls on primitive types. + new CallGraphChecks, + new DeadCodeElimination, // Replaces dead code by a `throw new DeadCodeEliminated` new Simplify), // Perform local optimizations, simplified versions of what linker does. List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments // Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here diff --git a/compiler/src/dotty/tools/dotc/FromTasty.scala b/compiler/src/dotty/tools/dotc/FromTasty.scala index 6f74a5d46ea2..3f22c08a1720 100644 --- a/compiler/src/dotty/tools/dotc/FromTasty.scala +++ b/compiler/src/dotty/tools/dotc/FromTasty.scala @@ -15,7 +15,7 @@ import util._ import reporting.Reporter import Decorators._ import dotty.tools.dotc.transform.Pickler -import tasty.DottyUnpickler +import tasty.{DottyUnpickler, TastyBuffer} import ast.tpd._ import NameKinds.QualifiedName @@ -101,4 +101,13 @@ object FromTasty extends Driver { } } } + + def compilationUnit(clsd: ClassDenotation, unpickler: DottyUnpickler)(implicit ctx: Context): CompilationUnit = { + val List(unpickled) = unpickler.body(ctx.addMode(Mode.ReadPositions)) + val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq())) + unit1.tpdTree = unpickled + unit1.unpicklers += (clsd.classSymbol -> unpickler.unpickler) + force.traverse(unit1.tpdTree) + unit1 + } } diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index ed05140dcfeb..16764b739668 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -43,6 +43,12 @@ class ScalaSettings extends Settings.SettingGroup { val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax") val silentWarnings = BooleanSetting("-nowarn", "Silence all warnings.") + val linkDCE = BooleanSetting("-link-dce", "Enable dead code elimination.") + val linkDCEAggressive = BooleanSetting("-link-aggressive-dce", "Enable aggressive dead code elimination.") + val linkVis = BooleanSetting("-link-vis", "Output the visual representation of the call graph.") + val linkJavaConservative = BooleanSetting("-link-java-conservative", "Compute call graph for java methods.") + val linkSpecialize = BooleanSetting("-link-specialize", "Enable link time specialization.") + /** -X "Advanced" settings */ val Xhelp = BooleanSetting("-X", "Print a synopsis of advanced options.") @@ -105,6 +111,9 @@ class ScalaSettings extends Settings.SettingGroup { val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).") val YshowVarBounds = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds") val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.") + val YlinkDCEChecks = BooleanSetting("-Ylink-dce-checks", "Check number of reachable classes and methods.") + val YlinkSpecialize = IntSetting("-YlinkSpecialize","Specialize methods with maximum this amount of polymorphic types.", 0, 0 to 10) + /** Linker specific flags */ val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ebfbf5926851..6689103e4b9b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -355,6 +355,8 @@ class Definitions { def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol def newGenericArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newGenericArray") def newArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newArray") + def DottyArraysMethods(implicit ctx: Context) = List(newArrayMethod, newGenericArrayMethod) + lazy val NilModuleRef = ctx.requiredModuleRef("scala.collection.immutable.Nil") def NilModule(implicit ctx: Context) = NilModuleRef.symbol @@ -388,7 +390,7 @@ class Definitions { def ArrayConstructor(implicit ctx: Context) = ArrayConstructorR.symbol lazy val ArrayModuleType = ctx.requiredModuleRef("scala.Array") def ArrayModule(implicit ctx: Context) = ArrayModuleType.symbol.moduleClass.asClass - + def ArrayMethods(implicit ctx: Context) = List(Array_apply, Array_update, Array_length, Array_clone, ArrayConstructor) lazy val UnitType: TypeRef = valueTypeRef("scala.Unit", BoxedUnitType, java.lang.Void.TYPE, UnitEnc) def UnitClass(implicit ctx: Context) = UnitType.symbol.asClass @@ -458,6 +460,12 @@ class Definitions { def BoxedUnit_UNIT(implicit ctx: Context) = BoxedUnitClass.linkedClass.requiredValue("UNIT") + + def isPrimitiveClass(sym: Symbol): Boolean = { + sym == defn.IntClass || sym == defn.LongClass || sym == defn.ShortClass || sym == defn.CharClass || sym == defn.ByteClass || + sym == defn.BooleanClass || sym == defn.FloatClass || sym == defn.DoubleClass || sym == defn.UnitClass + } + lazy val BoxedBooleanType: TypeRef = ctx.requiredClassRef("java.lang.Boolean") def BoxedBooleanClass(implicit ctx: Context) = BoxedBooleanType.symbol.asClass lazy val BoxedByteType: TypeRef = ctx.requiredClassRef("java.lang.Byte") @@ -600,6 +608,8 @@ class Definitions { def ContravariantBetweenAnnot(implicit ctx: Context) = ContravariantBetweenAnnotType.symbol.asClass lazy val DeprecatedAnnotType = ctx.requiredClassRef("scala.deprecated") def DeprecatedAnnot(implicit ctx: Context) = DeprecatedAnnotType.symbol.asClass + lazy val EntryPointAnnotType = ctx.requiredClassRef("scala.EntryPoint") + def EntryPointAnnot(implicit ctx: Context) = EntryPointAnnotType.symbol.asClass lazy val ImplicitNotFoundAnnotType = ctx.requiredClassRef("scala.annotation.implicitNotFound") def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass lazy val InlineAnnotType = ctx.requiredClassRef("scala.inline") diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 543b15fb1460..313536c1cec2 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -284,6 +284,8 @@ object NameKinds { val SkolemName = new UniqueNameKind("?") val LiftedTreeName = new UniqueNameKind("liftedTree") val SuperArgName = new UniqueNameKind("$superArg$") + val SpecializedName = new UniqueNameKind("$spec") + val DefaultExceptionName = new UniqueNameKind(nme.DEFAULT_EXCEPTION_NAME.toString) /** A kind of unique extension methods; Unlike other unique names, these can be * unmangled. diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index bf33471cc98f..6c3a46a1b33f 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -13,6 +13,7 @@ import config.Printers.config import scala.collection.mutable.{ListBuffer, ArrayBuffer} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform} import dotty.tools.dotc.transform._ +import dotty.tools.dotc.transform.linker._ import Periods._ import typer.{FrontEnd, RefChecks} import ast.tpd @@ -243,6 +244,7 @@ object Phases { private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter]) private val gettersCache = new PhaseCache(classOf[Getters]) private val genBCodeCache = new PhaseCache(classOf[GenBCode]) + private val summariesCache = new PhaseCache(classOf[CollectSummaries]) def typerPhase = typerCache.phase def picklerPhase = picklerCache.phase @@ -257,6 +259,7 @@ object Phases { def explicitOuterPhase = explicitOuterCache.phase def gettersPhase = gettersCache.phase def genBCodePhase = genBCodeCache.phase + def summariesPhase = summariesCache.phase def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id } diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index a0ab31b70105..0d1a6d76f10c 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -433,9 +433,9 @@ object StdNames { val info: N = "info" val inlinedEquals: N = "inlinedEquals" val isArray: N = "isArray" + val isDefined: N = "isDefined" val isDefinedAt: N = "isDefinedAt" val isDefinedAtImpl: N = "$isDefinedAt" - val isDefined: N = "isDefined" val isEmpty: N = "isEmpty" val isInstanceOf_ : N = "isInstanceOf" val java: N = "java" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 154f6ad90c41..a9e70ebabdd4 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -18,6 +18,8 @@ import util.Stats import java.util.WeakHashMap import config.Config import config.Printers.{completions, incremental, noPrinter} +import classfile.ClassfileParser +import dotty.tools.dotc.core.tasty.DottyUnpickler trait SymDenotations { this: Context => import SymDenotations._ @@ -1236,6 +1238,8 @@ object SymDenotations { initRunId: RunId) extends SymDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) { + private[dotc] var dottyUnpickler: Option[DottyUnpickler] = None + import util.LRUCache // ----- caches ------------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ac3245c37db3..87fd5bd265c4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1935,6 +1935,18 @@ object Types { class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithFixedSym class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym + /* used by linker */ + final class TypeRefWithFixedSymAndInfo(prefix: Type, name: TypeName, fixedSym: TypeSymbol, val underl: Type) extends TypeRefWithFixedSym(prefix, name, fixedSym) { + override def derivedSelect(prefix: Type)(implicit ctx: Context): Type = { + assert(prefix eq this.prefix, "loss in precision") + this + } + + override def info(implicit ctx: Context): Type = underl + + override def equals(that: Any): Boolean = that.isInstanceOf[TypeRefWithFixedSymAndInfo] && super.equals(that) && this.underl == that.asInstanceOf[TypeRefWithFixedSymAndInfo].underl + } + /** Assert current phase does not have erasure semantics */ private def assertUnerased()(implicit ctx: Context) = if (Config.checkUnerased) assert(!ctx.phase.erasedTypes) @@ -2045,6 +2057,10 @@ object Types { */ def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = unique(new TypeRefWithFixedSym(prefix, name, sym)) + + def withFixedSymAndInfo(prefix: Type, name: TypeName, sym: TypeSymbol, info: Type)(implicit ctx: Context): TypeRef = { + unique(new TypeRefWithFixedSymAndInfo(prefix, name, sym, info)) + } /** Create a type ref referring to given symbol with given name. * This is very similar to TypeRef(Type, Symbol), @@ -3440,22 +3456,28 @@ object Types { // ----- Annotated and Import types ----------------------------------------------- /** An annotated type tpe @ annot */ - case class AnnotatedType(tpe: Type, annot: Annotation) - extends UncachedProxyType with ValueType { + abstract case class AnnotatedType(tpe: Type, annot: Annotation) + extends CachedProxyType with ValueType { // todo: cache them? but this makes only sense if annotations and trees are also cached. override def underlying(implicit ctx: Context): Type = tpe - def derivedAnnotatedType(tpe: Type, annot: Annotation) = + def derivedAnnotatedType(tpe: Type, annot: Annotation)(implicit ctx: Context) = if ((tpe eq this.tpe) && (annot eq this.annot)) this else AnnotatedType(tpe, annot) override def stripTypeVar(implicit ctx: Context): Type = derivedAnnotatedType(tpe.stripTypeVar, annot) override def stripAnnots(implicit ctx: Context): Type = tpe.stripAnnots + + override def computeHash: Int = doHash(annot, tpe) } + class CachedAnnotatedType(tpe: Type, annot: Annotation) extends AnnotatedType(tpe, annot) + object AnnotatedType { - def make(underlying: Type, annots: List[Annotation]) = - (underlying /: annots)(AnnotatedType(_, _)) + def apply(tpe: Type, annot: Annotation)(implicit ctx: Context) = + unique(new CachedAnnotatedType(tpe, annot)) + def make(underlying: Type, annots: List[Annotation])(implicit ctx: Context) = + (underlying /: annots)(apply(_, _)) } // Special type objects and classes ----------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index ba58393a4c39..7ea8532d15d3 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -6,6 +6,7 @@ package classfile import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._ import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._ import NameKinds.{ModuleClassName, DefaultGetterName} +import tasty.DottyUnpickler import ast.tpd._ import java.io.{ ByteArrayInputStream, DataInputStream, File, IOException } import java.lang.Integer.toHexString @@ -145,6 +146,13 @@ class ClassfileParser( setClassInfo(moduleRoot, staticInfo) } + result match { + case result @ Some(_: DottyUnpickler) => + classRoot.dottyUnpickler = result.asInstanceOf[Option[DottyUnpickler]] + moduleRoot.dottyUnpickler = result.asInstanceOf[Option[DottyUnpickler]] + case _ => + } + // eager load java enum definitions for exhaustivity check of pattern match if (isEnum) { instanceScope.toList.map(_.ensureCompleted()) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala index 8178019aa033..5ef15fd9c8ff 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala @@ -11,6 +11,9 @@ import util.{SourceFile, NoSource} import Annotations.Annotation import core.Mode import classfile.ClassfileParser +import dotty.tools.dotc.transform.linker.summaries.MethodSummary +import dotty.tools.dotc.transform.linker.summaries.TastySummaries +import scala.collection.mutable object DottyUnpickler { @@ -27,6 +30,13 @@ object DottyUnpickler { def unpickle(reader: TastyReader, nameAtRef: NameTable) = new PositionUnpickler(reader) } + + class SummariesTreeSectionUnpickler(symAtAddr: mutable.HashMap[Addr, Symbol], sectionName: String) + extends TreeSectionUnpickler(posUnpickler = None) { + override def unpickle(reader: TastyReader, tastyName: NameTable): SummariesTreeUnpickler = { + new SummariesTreeUnpickler(symAtAddr, reader, tastyName, sectionName) + } + } } /** A class for unpickling Tasty trees and symbols. @@ -57,4 +67,20 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded { myBody } else computeBody() } + + def summaries(implicit ctx: Context): List[MethodSummary] = { + val sectionName = TastySummaries.sectionName + val tastySection = unpickler.unpickle(new SummariesTreeSectionUnpickler(treeUnpickler.symAtAddr, sectionName)).get + tastySection.asInstanceOf[SummariesTreeUnpickler].getStartReader(ctx) match { + case Some(treeReader) => + val unp = new TastyUnpickler.SectionUnpickler[List[MethodSummary]](sectionName) { + def unpickle(reader: TastyReader, tastyName: NameTable): List[MethodSummary] = + new TastySummaries.SummaryReader(treeReader, reader)(ctx).read() + } + unpickler.unpickle(unp).getOrElse(Nil) + + case None => Nil + } + + } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/SummariesTreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/SummariesTreeUnpickler.scala new file mode 100644 index 000000000000..64e865c08cb3 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/tasty/SummariesTreeUnpickler.scala @@ -0,0 +1,34 @@ +package dotty.tools.dotc.core.tasty + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Mode +import dotty.tools.dotc.core.Names.TermName +import dotty.tools.dotc.core.tasty.TastyBuffer.{Addr, NameRef} + +import scala.collection.mutable + +class SummariesTreeUnpickler(override val symAtAddr: mutable.HashMap[Addr, Symbol], reader: TastyReader, tastyName: NameRef => TermName, sectionName: String) + extends TreeUnpickler(reader, tastyName, posUnpicklerOpt = None) { + + roots = Set.empty + + def getStartReader(implicit ctx: Context): Option[TreeReader] = { + val st = new TreeReader(reader) + st.skipToplevel()(ctx.addMode(Mode.AllowDependentFunctions)) + + while (true) { + while (reader.nextByte != TastyFormat.VALDEF && !reader.isAtEnd) st.skipTree() + if (reader.isAtEnd) return None // no section here + val tag = reader.readByte() + val end = reader.readEnd() + val name = st.readName() + if (name.toString == sectionName) return Some(st.forkAt(end)) + st.skipTree() // skip type + st.skipTree() // skip rhs + } + + None + } + +} diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 338a395ab685..7cfce4bc9bc8 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -153,6 +153,11 @@ class TreePickler(pickler: TastyPickler) { writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) pickleSymRef(sym) } + else if (tpe.isTerm) { + writeByte(TERMREF) // should be changed to a new entry that keeps track of prefix, symbol & owner + pickleName(tpe.name) + pickleType(tpe.prefix) + } else { assert(tpe.symbol.isClass) assert(tpe.symbol.is(Flags.Scala2x), tpe.symbol.showLocated) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d1f800c9926f..82b4dca0ac41 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -29,7 +29,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi import tpd._ /** A map from addresses of definition entries to the symbols they define */ - private val symAtAddr = new mutable.HashMap[Addr, Symbol] + private[tasty] val symAtAddr = new mutable.HashMap[Addr, Symbol] /** A temporary map from addresses of definition entries to the trees they define. * Used to remember trees of symbols that are created by a completion. Emptied @@ -46,7 +46,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi /** The root symbol denotation which are defined by the Tasty file associated with this * TreeUnpickler. Set by `enterTopLevel`. */ - private var roots: Set[SymDenotation] = null + private[tasty] var roots: Set[SymDenotation] = null /** The root symbols that are defined in this Tasty file. This * is a subset of `roots.map(_.symbol)`. @@ -66,13 +66,13 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi this.roots = roots var rdr = new TreeReader(reader).fork ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr) - rdr.indexStats(reader.endAddr) + rdr.indexTopLevel } /** The unpickled trees */ def unpickle()(implicit ctx: Context): List[Tree] = { assert(roots != null, "unpickle without previous enterTopLevel") - new TreeReader(reader).readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions)) + new TreeReader(reader).forkAt(Addr(0)).readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions)) } class Completer(owner: Symbol, reader: TastyReader) extends LazyType { @@ -433,7 +433,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi def adjustIfModule(completer: LazyType) = if (flags is Module) ctx.adjustModuleCompleter(completer, name) else completer val sym = - roots.find(root => (root.owner eq ctx.owner) && root.name == name) match { + roots.find(root => root.exists && (root.owner eq ctx.owner) && root.name == name) match { case Some(rootd) => pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}") rootd.info = adjustIfModule( @@ -531,6 +531,17 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi (flags, annots.toList, privateWithin) } + /** Create symbols for the definitions in the top level statements. + */ + def indexTopLevel(implicit ctx: Context): Unit = { + nextByte match { + case PACKAGE => + processPackage { (pid, end) => implicit ctx => indexStats(end) } + case _ => + skipTree() + } + } + /** Create symbols for the definitions in the statement sequence between * current address and `end`. * @return the largest subset of {NoInits, PureInterface} that a diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index dfd2fda15a02..211a3e7619fb 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -266,6 +266,8 @@ class PlainPrinter(_ctx: Context) extends Printer { else "{...}.this" // TODO move underlying type to an addendum, e.g. ... z3 ... where z3: ... case tp: SkolemType => if (homogenizedView) toText(tp.info) else toText(tp.repr) + case tp: SingletonType => + toText(tp.underlying) } } diff --git a/compiler/src/dotty/tools/dotc/transform/OuterSpecializer.scala b/compiler/src/dotty/tools/dotc/transform/OuterSpecializer.scala new file mode 100644 index 000000000000..66f6bf9f3c10 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/OuterSpecializer.scala @@ -0,0 +1,816 @@ +package dotty.tools.dotc.transform + +import java.util + +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.{TreeTypeMap, tpd} +import dotty.tools.dotc.core.Contexts.{Context, ContextBase} +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.DenotTransformers.InfoTransformer +import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.core.NameKinds._ +import dotty.tools.dotc.core.Names.{Name, TypeName} +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core._ +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Flags.FlagSet +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols.{ClassSymbol, Symbol} +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform} +import dotty.tools.dotc.transform.linker.callgraph.{CallGraph, OuterTargs, SubstituteByParentMap} + +import scala.collection.mutable + +object OuterSpecializer { + def isPhaseRequired(implicit ctx: Context): Boolean = + ctx.settings.linkSpecialize.value +} + +// TODO: Check secondary constructors. +// TODO: check private fields +class OuterSpecializer extends MiniPhaseTransform with InfoTransformer { + import OuterSpecializer._ + import tpd._ + + override def phaseName = "specializeClass" + + type Specialization = Array[Type] + + /** + * Methods requested for specialization + * Generic Symbol => List[ (position in list of args, specialized type requested) ] + */ + private val specializationRequests: mutable.HashMap[Symbol, List[OuterTargs]] = mutable.HashMap.empty + + /** + * A map that links symbols to their specialized variants. + * Each symbol maps to another map, from the list of specialization types to the specialized symbol. + * Generic symbol => + * Map{ List of [ Tuple(position in list of args, specialized Type) ] for each variant => Specialized Symbol } + */ + val newSymbolMap: mutable.HashMap[Symbol, mutable.HashMap[OuterTargs, Symbol]] = mutable.HashMap.empty + + /** + * A map that links symbols to their speciazation requests. + * Each symbol maps to another map, from the list of specialization types to the specialized symbol. + * Generic symbol => + * Map{ List of [ Tuple(position in list of args, specialized Type) ] for each variant => Specialized Symbol } + */ + val outerBySym: mutable.HashMap[Symbol, OuterTargs] = mutable.HashMap.empty + + val addBridges: mutable.HashMap[ClassSymbol, List[(Symbol, Symbol)]] = mutable.HashMap.empty + + /** maps bridges back to original symbol */ + val canonical: mutable.HashMap[Symbol, Symbol] = mutable.HashMap.empty + + val originBySpecialized: mutable.HashMap[Symbol, Symbol] = mutable.HashMap.empty + + /** + * A list of symbols gone through the specialisation pipeline + * Is used to make calls to transformInfo idempotent + */ + private val processed: util.IdentityHashMap[Symbol, Type] = new util.IdentityHashMap() + + + def isSpecializable(sym: Symbol, numOfTypes: Int)(implicit ctx: Context): Boolean = + numOfTypes > 0 && + sym.name != nme.asInstanceOf_ && + !newSymbolMap.contains(sym) && + !(sym is Flags.JavaDefined) && + !sym.isPrimaryConstructor + + /** Get list of types to specialize for */ + def getSpecTypes(method: Symbol, poly: PolyType)(implicit ctx: Context): List[OuterTargs] = { + + val requested = specializationRequests.getOrElse(method, List.empty) + if (requested.nonEmpty) { + requested + } + else { + Nil + } + } + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = + if (isPhaseRequired) this else TreeTransforms.NoTransform + + /** was decl requested to be specialized */ + def requestedSpecialization(decl: Symbol)(implicit ctx: Context): Boolean = { + ctx.settings.YlinkSpecialize.value != 0 || specializationRequests.contains(decl) || { + originBySpecialized.getOrElse(decl, null) match { + case null => false + case origin if !origin.isClass => requestedSpecialization(origin) // a specialized version of specialized method todo: Am i right? + case _ => false + } + } + } + + def isSimilar(arguments: OuterTargs, other: OuterTargs)(implicit ctx: Context) = { + other.mp.forall { other => + arguments.mp.get(other._1) match { + case None => false + case Some(mapping) => + mapping.forall { thisMapping => + other._2.get(thisMapping._1) match { + case None => false + case Some(otherTp) => + otherTp.dropAlias =:= thisMapping._2.dropAlias + } + } + } + } + } + + def subsumes(arguments: OuterTargs, other: OuterTargs)(implicit ctx: Context) = { + other.mp.forall { other => + arguments.mp.get(other._1) match { + case None => false + case Some(mapping) => + mapping.forall { thisMapping => + other._2.get(thisMapping._1) match { + case None => false + case Some(otherTp) => + otherTp.dropAlias <:< thisMapping._2.dropAlias + } + } + } + } + } + + def specializationRequest(callGraph: CallGraph)(implicit ctx: Context): Unit = { + callGraph.reachableMethods.foreach { mc => + val methodSym = mc.call.termSymbol + val outerTargs = methodSym.info.widen match { + case PolyType(names, _) => + (names.map(_.paramName) zip mc.targs).foldLeft(mc.outerTargs)((x, nameType) => x.add(methodSym, nameType._1, nameType._2)) + case _ => + mc.outerTargs + } + if (outerTargs.mp.nonEmpty && !methodSym.isPrimaryConstructor) + registerSpecializationRequest(methodSym)(outerTargs) + } + callGraph.reachableTypes.foreach { tpc => + if (!tpc.tp.typeSymbol.is(Flags.JavaDefined)) { + val parentOverrides = tpc.tp.typeMembers(ctx).foldLeft(OuterTargs.empty)((outerTargs, denot) => + if (!denot.exists || (denot.symbol.owner eq defn.ScalaShadowingPackageClass)) outerTargs // TODO get outer targs from shadowed classes + else { + denot.symbol.allOverriddenSymbols.foldLeft(outerTargs)((outerTargs, sym) => + outerTargs.add(sym.owner, denot.symbol.name, denot.info)) + } + ) + + val spec = tpc.outerTargs ++ parentOverrides ++ OuterTargs.parentRefinements(tpc.tp) + + if (spec.nonEmpty) { + registerSpecializationRequest(tpc.tp.typeSymbol)(spec) + + def loop(remaining: List[Symbol]): Unit = { + if (remaining.isEmpty) return; + val target = remaining.head + + val nspec = new OuterTargs(spec.mp.filter { x => target.derivesFrom(x._1) }) + if (nspec.nonEmpty) + registerSpecializationRequest(target)(nspec) + loop(remaining.tail) + } + + val parents = tpc.tp.baseClasses + loop(parents) + } + } + } + } + + def registerSpecializationRequest(methodOrClass: Symbol)(arguments: OuterTargs)(implicit ctx: Context): Unit = { + if (((methodOrClass.isClass && (!methodOrClass.is(Flags.Module) || !methodOrClass.isStatic)) || + methodOrClass.is(Flags.Method)) + && (methodOrClass.sourceFile ne null)) { + if (ctx.phaseId > this.treeTransformPhase.id) + assert(ctx.phaseId <= this.treeTransformPhase.id) + val prev = specializationRequests.getOrElse(methodOrClass, List.empty) + def isSimilar(arguments: OuterTargs, other: OuterTargs) = { + other.mp.forall { other => + arguments.mp.get(other._1) match { + case None => false + case Some(mapping) => + mapping.forall { thisMapping => + other._2.get(thisMapping._1) match { + case None => false + case Some(otherTp) => + otherTp =:= thisMapping._2 + } + } + } + } + } + + if (prev.exists(isSimilar(arguments, _))) + return; + + specializationRequests.put(methodOrClass, arguments :: prev) + } + else { + ctx.log(s"ignoring specialization reguest for ${methodOrClass.showFullName} for ${arguments}") + } + } + + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = { + if (!isPhaseRequired) return ref + + val n = super.transform(ref) + if (n.symbol.isClass && requestedSpecialization(n.symbol)) { + val sd = n.asInstanceOf[SymDenotation] + val newParent = n.symbol.asClass.baseClasses.find(x => x.isClass && !(x is Flags.Trait) && !requestedSpecialization(x)) + val info = n.info.asInstanceOf[ClassInfo] + // todo: fix parents + sd.copySymDenotation(initFlags = sd.flags | Flags.Trait, info = info) + } else n + } + + /* Provided a class that owns a method to be specialized, adds specializations to the body of the class, without forcing new symbols + * provided a method to be specialized, specializes it and enters it into its owner + * */ + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + if (!isPhaseRequired) return tp + + def enterNewSyms(newDecls: List[Symbol], classInfo: ClassInfo, tp: Type) = { + if (!classInfo.typeSymbol.is(Flags.Package)) { + val decls = classInfo.decls.cloneScope + newDecls.foreach(x => decls.enter(x)) + classInfo.derivedClassInfo(decls = decls) + } else { + newDecls.foreach(_.entered) + classInfo // used to return tp here. why? + } + } + + def duplicateClass(originalClass: ClassInfo, specialization: OuterTargs): ClassSymbol = { + val claz = originalClass.typeSymbol.asClass + + val newParents = originalClass.classParents.head :: claz.typeRef :: originalClass.classParents.tail + val map = new SubstituteByParentMap(specialization) + val newDecls = originalClass.decls.cloneScope.openForMutations // this is a hack. I'm mutating this scope later + def newType(nwClaz: ClassSymbol): Type = + ClassInfo(originalClass.prefix, nwClaz, newParents, newDecls, map.apply(originalClass.selfType)) + + def fixModule(nm: TypeName): TypeName = { + import NameOps._ + if (claz.flags is Flags.Module) nm.moduleClassName + else nm + } + val sepcName: TypeName = SpecializedName.fresh(claz.name.toTermName).toTypeName + val newClaz = ctx.newClassSymbol(claz.owner, fixModule(sepcName), claz.flags | Flags.Synthetic, newType) + + originalClass.decls.foreach { originalDecl => + + lazy val otherTr = originalDecl.typeRef + lazy val mappedTr = map(otherTr) + originalDecl match { + case other: Symbol if other.isType && !other.isClass && (otherTr ne mappedTr) => + other.info match { + case tp: TypeBounds => + // val newParam = ctx.newSymbol(newClaz, other.name, other.flags, TypeAlias(otherTr), other.privateWithin, other.coord) + val newBoulds = + if (mappedTr.typeSymbol eq defn.AnyClass) tp + else TypeAlias(mappedTr) + val nw = ctx.newSymbol(newClaz, other.name, other.flags, newBoulds, other.privateWithin, other.coord) + newDecls.replace(other, nw) + } + + case other => + val tpe = if (other.isClassConstructor) other.info match { + case oinfo: PolyType => + val newConstructorBounds = originalClass.typeParams.map(x => specialization.mp(claz)(x.paramName)) + val fullConstructorBounds = (oinfo.paramInfos zip newConstructorBounds).map { case (old, nw) => TypeBounds(old.lo | nw.dropAlias, old.hi & nw.dropAlias) } + def newResultType(m: MethodType): LambdaType = { + m.resultType match { + case r: MethodType => m.derivedLambdaType(m.paramNames, m.paramInfos, newResultType(r)) + case r: RefinedType => + m.derivedLambdaType(m.paramNames, m.paramInfos, r.translateParameterized(claz, newClaz)) + case r => ??? + } + } + val resultType = newResultType(oinfo.resultType.asInstanceOf[MethodType]) + oinfo.derivedLambdaType(oinfo.paramNames, fullConstructorBounds, resultType) + case _ => map(other.info) + } else map(other.info) + def fixMethodic(tp: Type, flags: FlagSet) = { + if (flags is Flags.Method) + if (tp.isInstanceOf[MethodicType]) tp + else ExprType(tp) + else tp + } + val nw = ctx.newSymbol(newClaz, other.name, other.flags, fixMethodic(tpe, other.flags), other.privateWithin, other.coord) + originBySpecialized.put(nw, other) + + val currentBridge = addBridges.getOrElse(claz, Nil).filter(x => x._2 == other && x._1.signature == nw.signature) + + if (!other.isClassConstructor && (nw.signature.matchDegree(other.signature) != Signature.FullMatch) && currentBridge.isEmpty) { + // bridge is needed + + { + val bridgeInSuper = ctx.newSymbol(claz, nw.name, nw.flags.&~(Flags.Accessor | Flags.ParamAccessor) | Flags.Bridge, nw.info).enteredAfter(this) + val lst = addBridges.getOrElse(claz, Nil) + canonical.put(bridgeInSuper, other) + addBridges.put(claz, (bridgeInSuper, other) :: lst) + // add spec? + } + + { + val bridgeInSub = + ctx.newSymbol(newClaz, nw.name, nw.flags.&~(Flags.Accessor | Flags.ParamAccessor) | Flags.Bridge, other.info) + val lst = addBridges.getOrElse(newClaz, Nil) + + // add spec? + + newDecls.enter(bridgeInSub) + canonical.put(bridgeInSub, nw) + addBridges.put(newClaz, (bridgeInSub, nw) :: lst) + } + + + } + + if (other.isTerm && !(other.is(Flags.Method))) { + // field + val newFlags = (other.symbol.flags &~ (Flags.Mutable)) | Flags.Deferred | Flags.Method | Flags.Stable + other.asSymDenotation.copySymDenotation(initFlags = newFlags, info = fixMethodic(other.info, newFlags)).installAfter(this) + } + + /* if (other.isTerm && other.is(Flags.ParamAccessor)) { + val newFlags = (other.symbol.flags &~ (Flags.ParamAccessor | Flags.Accessor | Flags.Private)) | Flags.Deferred | Flags.Method| Flags.Stable + other.asSymDenotation.copySymDenotation(initFlags = newFlags, info = fixMethodic(other.info, newFlags)).installAfter(this) + } */ + + newDecls.replace(other, nw) + } + } + + val umap: mutable.HashMap[OuterTargs, Symbol] = newSymbolMap.getOrElse(claz, mutable.HashMap.empty) + umap.put(specialization, newClaz) + newSymbolMap.put(claz, umap) + originBySpecialized.put(newClaz, claz) + + + val specializedByOrigin = newDecls.filter(x => x.isTerm && originBySpecialized.contains(x)). + map(x => (originBySpecialized(x), x)).toMap + + // update specialized mappings for subclass + newDecls.foreach(newSym => { + if (newSym.isTerm && !newSym.isPrimaryConstructor && !newSym.is(Flags.Bridge)) { + val oldSym = originBySpecialized(newSym) + val oldSpeciazlizations = newSymbolMap.get(oldSym) + oldSpeciazlizations match { + case None => + case Some(oldspecs) if oldspecs.nonEmpty=> + val newspec = oldspecs.map{case (oldargs, oldspecsym) => + val updatedOldArgs = oldargs.mp(oldSym) + (new OuterTargs((oldargs.mp - oldSym) + (newSym -> updatedOldArgs)) ++ specialization, + specializedByOrigin(oldspecsym)) + } + newSymbolMap.put(newSym, newspec) + } + } + }) + + newClaz + } + + def specializeSymbol(sym: Symbol): Type = { + processed.put(sym, NoType) + ctx.debuglog(s"started specializing type of $sym") + val ret = sym.info match { + case classInfo: ClassInfo => + + val newDecls = classInfo.decls + .filter(x => x.isDefinedInCurrentRun && x.isCompleted) // We do not want to force symbols. Unforced symbol are not used in the source + .filterNot(_.isConstructor) + .filter(requestedSpecialization) + .flatMap(decl => { + decl.info.widen match { + case poly: PolyType if isSpecializable(decl.symbol, poly.paramNames.length) => + generateMethodSpecializations(getSpecTypes(decl, poly))(poly, decl) + case claz: ClassInfo if requestedSpecialization(decl) => + def addGenericSpec(x: List[OuterTargs]): List[OuterTargs] = + if (x.isEmpty) x + else { + val tparams = decl.typeParams + val generic = x.find(_.mp.values.flatten.forall(x => TypeErasure.erasure(x._2) == defn.ObjectType)) + if (generic.isEmpty) { + val tparamsMapping: Map[Name, Type] = tparams.map(x => (x.name, TypeAlias(defn.AnyType))).toMap + new OuterTargs(Map(decl -> tparamsMapping)) :: x + } else x + } + val clazInfo = specializeSymbol(decl.asClass).asInstanceOf[ClassInfo] + /*addGenericSpec*/ (specializationRequests(decl)).map(x => duplicateClass(clazInfo, x)) + case _ => Nil + } + }) + + val ntp = + if (newDecls.nonEmpty) enterNewSyms(newDecls.toList, classInfo, tp) + else classInfo + ntp + case poly: PolyType if isSpecializable(sym, poly.paramNames.length) => // specialize method + if (sym.owner.info.isInstanceOf[ClassInfo]) { + transformInfo(sym.owner.info, sym.owner) //why does it ever need to recurse into owner? + tp + } + else if (requestedSpecialization(sym) && + isSpecializable(sym, poly.paramNames.length)) { + generateMethodSpecializations(getSpecTypes(sym, poly))(poly, sym) // todo: this value is discarded. a bug? + tp + } + else tp + case _ => tp + } + processed.put(sym, ret) + ctx.debuglog(s"finished specializing $sym") + ret + } + + def generateMethodSpecializations(specTypes: List[OuterTargs]) + (poly: PolyType, decl: Symbol) + (implicit ctx: Context): List[Symbol] = { + specTypes.map(x => generateSpecializedSymbol(x, poly, decl)) + } + + def generateSpecializedSymbol(instantiations: OuterTargs, poly: PolyType, decl: Symbol) + (implicit ctx: Context): Symbol = { + val resType = new SubstituteByParentMap(instantiations).apply(poly.resType) + + val bounds = if (instantiations.mp.contains(decl)) (poly.paramInfos zip poly.paramNames).map { case (bound, name) => + instantiations.mp.getOrElse(decl, Map.empty).get(name) match { + case Some(instantiation) => TypeBounds(bound.lo | instantiation, bound.hi & instantiation) + case None => bound + } + } else poly.paramInfos + val newSym = ctx.newSymbol( + decl.owner, + SpecializedName.fresh(decl.name.asTermName) + /*NameOps.NameDecorator(decl.name) + .specializedFor(Nil, Nil, instantiations.toList, poly.paramNames) + .asInstanceOf[TermName]*/ , + decl.flags | Flags.Synthetic, + poly.newLikeThis(poly.paramNames, bounds, resType) + ) + + val map: mutable.HashMap[OuterTargs, Symbol] = newSymbolMap.getOrElse(decl, mutable.HashMap.empty) + map.put(instantiations, newSym) + newSymbolMap.put(decl, map) + outerBySym.put(newSym, instantiations) + + newSym + } + + if (processed.containsKey(sym)) { + val v = processed.get(sym) + if (v eq NoType) + ctx.error("circular error") + v + } + + if (!processed.containsKey(sym) && + (sym ne defn.ScalaPredefModule.moduleClass) && + !(sym is(Flags.JavaDefined, Flags.Package)) && + !(sym is Flags.Scala2x) && + !sym.isAnonymousClass /*why? becasue nobody can call from outside? they can still be called from inside the class*/ ) { + specializeSymbol(sym) + } else tp + } + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + tree.tpe.widen match { + + case poly: PolyType + if !(tree.symbol.isPrimaryConstructor + || (tree.symbol is Flags.Label) + ) => + + def specialize(decl: Symbol): List[Tree] = { + if (newSymbolMap.contains(decl)) { + val specInfo = newSymbolMap(decl) + val newSyms = specInfo.values.toList + + + newSyms.map { newSym => + val newSymType = newSym.info.widenDealias + ctx.log(s"specializing ${tree.symbol.fullName} for ${newSymType.show}") + val typemap: (Type, List[Symbol], List[Type]) => (List[Symbol], List[Type]) => Type => Type = + (oldPoly, oldTparams, newTparams) => (oldArgs, newArgs) => + new SubstituteByParentMap(outerBySym(newSym)) { + override def apply(tp: Type): Type = { + val t = super.apply(tp) + .substDealias(oldTparams, newTparams) + val t2 = oldPoly match { + case oldPoly: PolyType => t.substParams(oldPoly, newTparams) + case _ => t + } + t2.subst(oldArgs, newArgs) + } + } + duplicateMethod(newSym, tree)(typeMap = typemap)() + } + } else Nil + } + val specializedTrees = specialize(tree.symbol) + Thicket(tree :: specializedTrees) + case _ => tree + } + } + + def duplicateMethod(newSym: Symbol, oldTree: DefDef) + (typeMap: (Type, List[Symbol], List[Type]) => (List[Symbol], List[Type]) => Type => Type) + (substFrom: List[Symbol] = Nil, substTo: List[Symbol] = Nil) + (implicit ctx: Context): DefDef = { + val oldSym = oldTree.symbol + originBySpecialized.put(newSym, oldSym) + val origTParams = oldTree.tparams.map(_.symbol) + val origVParams = oldTree.vparamss.flatten.map(_.symbol) + + def rhsFn(tparams: List[Type])(vparamss: List[List[Tree]]) = { + def treemap(tree: Tree): Tree = tree match { + case Return(t, from) if from.symbol == oldSym => Return(t, ref(newSym)) + case t: This if t.symbol eq oldSym.enclosingClass => This(newSym.enclosingClass.asClass) + case t => t + } + + val abstractPolyType = oldSym.info.widenDealias + val vparamTpes = vparamss.flatten.map(_.tpe) + + val typesReplaced = new TreeTypeMap( + treeMap = treemap, + typeMap = typeMap(abstractPolyType, origTParams, tparams)(origVParams, vparamTpes), + oldOwners = oldSym :: substFrom, + newOwners = newSym :: substTo + ).transform(oldTree.rhs) + + typesReplaced + } + polyDefDef(newSym.asTerm, rhsFn) + } + + private def newBridges(claz: ClassSymbol)(implicit ctx: Context) = { + val bridgeSymbols = addBridges.getOrElse(claz, Nil) + bridgeSymbols.map { case (nw, old) => + def rhsFn(tparams: List[Type])(vparamss: List[List[Tree]]) = { + val prefix = This(claz).select(old).appliedToTypes(tparams) + val argTypess = prefix.tpe.widen.paramInfoss + val argss = (vparamss zip argTypess).map { case (vparams, argTypes) => + (vparams zip argTypes).map { case (vparam, argType) => vparam.ensureConforms(argType) } + } + prefix.appliedToArgss(argss).ensureConforms(nw.info.finalResultType) + } + polyDefDef(nw.asTerm, rhsFn) + } + + } + + + override def transformTypeDef(tree: tpd.TypeDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + val oldSym = tree.symbol + if (oldSym.isClass) newSymbolMap.get(tree.symbol) match { + case Some(x) => + val newClasses: List[Tree] = x.iterator.map { case (outersTargs, newClassSym) => + def typemap(oldPoly: Type, oldTparams: List[Symbol], newTparams: List[Type])(oldArgs: List[Symbol], newArgs: List[Type]): SubstituteByParentMap = { + new SubstituteByParentMap(outersTargs) { + override def apply(tp: Type): Type = { + val t = super.apply(tp) + .substDealias(oldTparams, newTparams) + val t2 = oldPoly match { + case oldPoly: PolyType => t.substParams(oldPoly, newTparams) + case _ => t + } + t2.subst(oldArgs, newArgs) + } + } + } + //duplicateMethod(newSym, tree)(typeMap = typemap)() + val treeRhs = tree.rhs.asInstanceOf[Template] + val newSubst = newClassSym.info.fields.map(_.symbol).toList // ++ newSym.info.accessors + val oldSubst = oldSym.info.fields.map(_.symbol).toList // ++ oldSym.info.accessors + def treeMap(t: Tree): Tree = t match { + case t: This if t.symbol eq oldSym => tpd.This(newClassSym.asClass) + case _ => t + } + val bodytreeTypeMap = new TreeTypeMap(typeMap = typemap(null, Nil, Nil)(Nil, Nil), substFrom = oldSubst, substTo = newSubst, treeMap = treeMap + /*oldOwners = oldSubst, newOwners = newSubst*/) + val constr = duplicateMethod(newClassSym.primaryConstructor, treeRhs.constr)(typemap)(oldSubst, newSubst) + + val body = treeRhs.body.map { + case t: DefDef => + val variants = newClassSym.info.decl(t.symbol.name).suchThat(p => !(p is Flags.Bridge)) + val popa = variants.alternatives.map(_.symbol.info.overrides(t.symbol.info)) + val newMeth: Symbol = // todo: handle overloading + /*if(!t.symbol.is(Flags.Private)) t.symbol.matchingMember(newSym.info) // does not work. Signatures do not match anymore + else */ + t.symbol.asSymDenotation.matchingDecl(newClassSym, newClassSym.thisType).filter(p => !(p is Flags.Bridge)).orElse( + variants.symbol + ) + //x.matchingDecl(original.symbol.owner.asClass, x.owner.thisType).exists) + + duplicateMethod(newMeth, t)(typemap)(oldSubst, newSubst) + case t: TypeDef if !t.isClassDef => + val newMember = newClassSym.info.decl(t.symbol.name).asSymDenotation.symbol.asType + tpd.TypeDef(newMember) + case t: ValDef => + val newMember = newClassSym.info.decl(t.symbol.name).asSymDenotation.symbol.asTerm + tpd.ValDef(newMember, bodytreeTypeMap.apply(t.rhs)) + case t => // just body. TTM this shit + bodytreeTypeMap.apply(t) + } ++ newBridges(newClassSym.asClass) + val superArgs = treeRhs.parents.head match { + case Apply(fn, args) => args + case _ => Nil + } + + tpd.ClassDef(newClassSym.asClass, constr, body, superArgs) + }.toList + + val genericBridges = newBridges(oldSym.asClass) + + val newTrait = + if (genericBridges.nonEmpty) { + val currentRhs = tree.rhs.asInstanceOf[Template] + cpy.TypeDef(tree)(rhs = cpy.Template(currentRhs)(body = currentRhs.body ++ genericBridges)) + } else tree + + val ret = Thicket(newTrait :: newClasses) + ret + case None => + tree + } else tree + } + + def rewireTree(tree: Tree)(implicit ctx: Context): Tree = { + assert(tree.isInstanceOf[TypeApply]) + val TypeApply(fun, args) = tree + + val canonicalSymbol = canonical.getOrElse(fun.symbol, fun.symbol) + + if (canonicalSymbol.isPrimaryConstructor && newSymbolMap.contains(canonicalSymbol.owner)) { + val availableSpecializations = newSymbolMap(fun.symbol.owner) + val poly = canonicalSymbol.info.widen.asInstanceOf[PolyType] + val argsNames = canonicalSymbol.owner.asClass.classInfo.typeParams.map(_.paramName) zip args + val availableClasses = availableSpecializations.filter { + case (instantiations, symbol) => { + val mappings = instantiations.mp(canonicalSymbol.owner) + argsNames.forall { case (name, arg) => arg.tpe <:< mappings(name).dropAlias } + } + } + + val bestVersions = availableClasses.iterator.filter { case (instantiations1, symbol1) => + !availableClasses.exists { case (instantiations2, symbol2) => + (symbol2 ne symbol1) && subsumes(instantiations1, instantiations2) + } + }.toList + + val ideal = availableSpecializations.find { + case (instantiations, symbol) => { + val mappings = instantiations.mp(canonicalSymbol.owner) + argsNames.forall { case (name, arg) => arg.tpe =:= mappings(name).dropAlias } + } + } + + def rewrite(newClassSym: Symbol) = { + ctx.debuglog(s"new ${canonicalSymbol.owner} rewired to ${newClassSym}") + tpd.New(newClassSym.typeRef) + .select(newClassSym.primaryConstructor) // todo handle secondary cosntr + .appliedToTypeTrees(args) + } + + if (ideal.nonEmpty) { + rewrite(ideal.get._2) + } else if (bestVersions.length > 1) { + ctx.error(s"Several specialized variants fit for ${canonicalSymbol.name} of ${canonicalSymbol.owner}." + + s" Defaulting to no specialization. This is not supported yet. Variants: \n ${bestVersions.map { x => (x._2.name, x._2.info.widenDealias.show) }.mkString("\n")}") + tree + } else if (bestVersions.nonEmpty) { + val newClassSym = bestVersions.head._2 + ctx.debuglog(s"new ${canonicalSymbol.owner} rewired to ${newClassSym}") + tpd.New(newClassSym.typeRef) + .select(newClassSym.primaryConstructor) // todo handle secondary cosntr + .appliedToTypeTrees(args) + } else EmptyTree + } else if (newSymbolMap.contains(canonicalSymbol)) { + // not a constructor + val poly = fun.symbol.info.widen.asInstanceOf[PolyType] + val argsNames = poly.paramNames zip args + val availableSpecializations = newSymbolMap(canonicalSymbol) + val betterDefs = availableSpecializations.filter { + case (instantiations, symbol) => { + val mappings = instantiations.mp(canonicalSymbol) + argsNames.forall { case (name, arg) => arg.tpe <:< mappings(name) } + } + }.toList + + if (betterDefs.length > 1) { + ctx.debuglog(s"Several specialized variants fit for ${fun.symbol.name} of ${fun.symbol.owner}.") + } + + if (betterDefs.nonEmpty) { + val newFunSym = betterDefs.head._2 + ctx.debuglog(s"method ${fun.symbol.name} of ${fun.symbol.owner} rewired to specialized variant") + val prefix = fun match { + case Select(pre, name) => + pre + case t@Ident(_) if t.tpe.isInstanceOf[TermRef] => + val tp = t.tpe.asInstanceOf[TermRef] + if (tp.prefix ne NoPrefix) + ref(tp.prefix.termSymbol) + else EmptyTree + case _ => EmptyTree + } + if (prefix ne EmptyTree) prefix.select(newFunSym).appliedToTypeTrees(args) + else ref(newFunSym).appliedToTypeTrees(args) + } else tree + } else tree + + } + + def transormGenApply(tree: GenericApply[Type])(implicit ctx: Context): Tree = { + tree match { + case t: tpd.TypeApply => + val TypeApply(fun, _) = tree + if (fun.tpe.widenDealias.isParameterless) rewireTree(tree) + else tree + case t: tpd.Apply => + val Apply(fun, args) = tree + fun match { + case fun: TypeApply => + val typeArgs = fun.args + val newFun = rewireTree(fun) + if (fun ne newFun) + if (newFun ne EmptyTree) + Apply(newFun, args) + else { + Typed(ref(defn.Sys_errorR).appliedTo(Literal(Constant("should never be reached"))), TypeTree(tree.tpe)) + } + else tree + case fun: Apply => + Apply(transormGenApply(fun), args) + case _ => tree + } + } + } +} + +object OuterSpecializeParents { + def isPhaseRequired(implicit ctx: Context): Boolean = + OuterSpecializer.isPhaseRequired +} + +class OuterSpecializeParents extends MiniPhaseTransform with InfoTransformer { + import OuterSpecializer._ + + var specPhase: OuterSpecializer = null + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = + if (isPhaseRequired) this else TreeTransforms.NoTransform + + override def init(base: ContextBase, id: Int): Unit = { + specPhase = base.phaseOfClass(classOf[OuterSpecializer]).asInstanceOf[OuterSpecializer] + super.init(base, id) + } + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = { + if (!isPhaseRequired) return tp + + if (sym.isClass && specPhase.originBySpecialized.contains(sym)) { + val origSym = specPhase.originBySpecialized(sym) + val specialization: OuterTargs = specPhase.newSymbolMap(origSym).find(_._2 == sym).get._1 + + tp match { + case classInfo: ClassInfo => + val classParent :: original :: others = classInfo.classParents + + def betterParent (parent: TypeRef): TypeRef = { + specPhase.newSymbolMap.get(parent.typeSymbol) match { + case None => parent + case Some(variants) => + /* same as in CollectSummaries.sendSpecializationRequests */ + def filterApplies(spec: OuterTargs, parent: Symbol) = { + new OuterTargs(spec.mp.filter{x => parent.derivesFrom(x._1)}) + } + + variants.find { case (outerTargs, newSym) => + specPhase.isSimilar(filterApplies(specialization, parent.typeSymbol), filterApplies(outerTargs, parent.typeSymbol)) + }.map(_._2.typeRef).getOrElse(parent) + } + } + val mappedParents: List[TypeRef] = betterParent(classParent) :: original :: others.map(betterParent) + classInfo.derivedClassInfo(classParents = mappedParents) + case _ => + ??? + } + } else tp + } + + val phaseName: String = "specializeClassParents" + + override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = specPhase.transormGenApply(tree) + + override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = specPhase.transormGenApply(tree) +} diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index 76b1658d1558..d50e609cacf9 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -26,9 +26,7 @@ class Pickler extends Phase { s.close } - // Maps that keep a record if -Ytest-pickler is set. private val beforePickling = new mutable.HashMap[ClassSymbol, String] - private val picklers = new mutable.HashMap[ClassSymbol, TastyPickler] /** Drop any elements of this list that are linked module classes of other elements in the list */ private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = { @@ -43,25 +41,17 @@ class Pickler extends Phase { for { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree)) tree <- sliceTopLevel(unit.tpdTree, cls) } { + if (ctx.settings.YtestPickler.value) beforePickling(cls) = tree.show val pickler = new TastyPickler() - if (ctx.settings.YtestPickler.value) { - beforePickling(cls) = tree.show - picklers(cls) = pickler - } + unit.picklers += (cls -> pickler) val treePkl = pickler.treePkl treePkl.pickle(tree :: Nil) - treePkl.compactify() pickler.addrOfTree = treePkl.buf.addrOfTree - pickler.addrOfSym = treePkl.addrOfSym if (tree.pos.exists) new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil) - // other pickle sections go here. - val pickled = pickler.assembleParts() - unit.pickled += (cls -> pickled) - def rawBytes = // not needed right now, but useful to print raw format. - pickled.iterator.grouped(10).toList.zipWithIndex.map { + pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map { case (row, i) => s"${i}0: ${row.mkString(" ")}" } // println(i"rawBytes = \n$rawBytes%\n%") // DEBUG @@ -75,7 +65,7 @@ class Pickler extends Phase { override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { val result = super.runOn(units) if (ctx.settings.YtestPickler.value) - testUnpickler( + testUnpickler(units)( ctx.fresh .setPeriod(Period(ctx.runId + 1, FirstPhaseId)) .setReporter(new ThrowingReporter(ctx.reporter)) @@ -84,11 +74,11 @@ class Pickler extends Phase { result } - private def testUnpickler(implicit ctx: Context): Unit = { + private def testUnpickler(units: List[CompilationUnit])(implicit ctx: Context): Unit = { pickling.println(i"testing unpickler at run ${ctx.runId}") ctx.initialize() val unpicklers = - for ((cls, pickler) <- picklers) yield { + for (unit <- units; (cls, pickler) <- unit.picklers) yield { val unpickler = new DottyUnpickler(pickler.assembleParts()) unpickler.enter(roots = Set()) cls -> unpickler diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index b05de26a7b04..f803109241c2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -32,6 +32,7 @@ import config.Printers import java.lang.AssertionError import dotty.tools.dotc.core.Names +import linker.DeadCodeElimination import scala.util.control.NonFatal @@ -376,7 +377,9 @@ class TreeChecker extends Phase with SymTransformer { !x.isCompanionMethod && !x.isValueClassConvertMethod - val symbolsNotDefined = cls.classInfo.decls.toList.toSet.filter(isNonMagicalMethod) -- impl.body.map(_.symbol) - constr.symbol + val dce = ctx.phaseOfClass(classOf[DeadCodeElimination]).asInstanceOf[DeadCodeElimination] + + val symbolsNotDefined = cls.classInfo.decls.toList.toSet.filter(x => isNonMagicalMethod(x) && !dce.wasAggressivelyDCEd(x)) -- impl.body.map(_.symbol) - constr.symbol assert(symbolsNotDefined.isEmpty, i" $cls tree does not define methods: ${symbolsNotDefined.toList}%, %\n" + diff --git a/compiler/src/dotty/tools/dotc/transform/linker/BuildCallGraph.scala b/compiler/src/dotty/tools/dotc/transform/linker/BuildCallGraph.scala new file mode 100644 index 000000000000..a231b4f109a0 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/BuildCallGraph.scala @@ -0,0 +1,129 @@ +package dotty.tools.dotc.transform.linker + +import dotty.tools.backend.jvm.CollectEntryPoints +import dotty.tools.dotc.{CompilationUnit, FromTasty} +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.SymDenotations.ClassDenotation +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.tasty.DottyUnpickler +import dotty.tools.dotc.transform.OuterSpecializer +import dotty.tools.dotc.transform.linker.callgraph._ + +object BuildCallGraph { + def withJavaCallGraph(implicit ctx: Context): Boolean = + ctx.settings.linkJavaConservative.value + + def withOutEdges(implicit ctx: Context): Boolean = + ctx.settings.linkVis.value + + def isPhaseRequired(implicit ctx: Context): Boolean = { + DeadCodeElimination.isPhaseRequired || CallGraphChecks.isPhaseRequired || + OuterSpecializer.isPhaseRequired || ctx.settings.linkVis.value + } + + def listPhase(implicit ctx: Context): List[Phase] = { + if (isPhaseRequired) List(new BuildCallGraph) + else Nil + } +} + +class BuildCallGraph extends Phase { + import BuildCallGraph._ + + private var callGraph: CallGraph = _ + + def phaseName: String = "callGraph" + + def getCallGraph: CallGraph = callGraph + + /** + * @param mode see modes above + * @param specLimit how many specializations symbol can have max + * @return (reachableMethods, reachableTypes, casts, outerMethod) + */ + private def buildCallGraph(mode: Int, specLimit: Int)(implicit ctx: Context): CallGraph = { + val startTime = java.lang.System.currentTimeMillis() + + val collectedSummaries = ctx.summariesPhase.asInstanceOf[CollectSummaries].methodSummaries + + val callGraphBuilder = new CallGraphBuilder(collectedSummaries, mode, specLimit, withJavaCallGraph, withOutEdges) + + lazy val scalaApp = ctx.requiredClass("scala.App".toTypeName) + lazy val scalaUtilPropertiesTrait = ctx.requiredClass("scala.util.PropertiesTrait".toTypeName) + def isEntryPoint(s: Symbol): Boolean = { + def filteredMain = (s.owner eq scalaApp) || (s.owner eq scalaUtilPropertiesTrait) + ((s.name eq nme.main) /* for speed */ && s.is(Method) && CollectEntryPoints.isJavaMainMethod(s) && !filteredMain) || // Java main method + (s.is(Method) && s.hasAnnotation(defn.EntryPointAnnot)) // Explicit entry point + } + + var entryPointId = 0 + for (x <- collectedSummaries.valuesIterator) { + if (isEntryPoint(x.methodDef)) { + entryPointId += 1 + callGraphBuilder.pushEntryPoint(x.methodDef, entryPointId) + x.methodDef.owner.ownersIterator.foreach { owner => + if (owner.is(Module) && !owner.isEmptyPackage) { + val sourceModule = owner.sourceModule + val moduleEntryPoint = + if (sourceModule.owner.exists && !sourceModule.owner.isEmptyPackage) sourceModule + else if (owner.primaryConstructor.exists) owner.primaryConstructor // workaround for modules in the empty package + else NoSymbol + if (moduleEntryPoint.exists) + callGraphBuilder.pushEntryPoint(moduleEntryPoint, entryPointId) + } + } + } + } + + callGraphBuilder.build() + + val endTime = java.lang.System.currentTimeMillis() + ctx.log("++++++++++ finished in " + (endTime - startTime)/1000.0 +" seconds. ++++++++++ ") + + callGraphBuilder.result() + } + + override def run(implicit ctx: Context): Unit = () + + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { + if (!BuildCallGraph.isPhaseRequired) units + else { + val mode = CallGraphBuilder.AnalyseArgs + val specLimit = 15 + + ctx.log(s"\n\t\t\tType & Arg flow analysis") + + val callGraph = buildCallGraph(mode, specLimit) + this.callGraph = callGraph + + if (OuterSpecializer.isPhaseRequired) { + val outerSpecializer = ctx.phaseOfClass(classOf[OuterSpecializer]).asInstanceOf[OuterSpecializer] + outerSpecializer.specializationRequest(callGraph) + } + + callGraph.getInfo().log() + + if (ctx.settings.linkVis.value) { + val visFile = new java.io.File(ctx.settings.d.value + "/call-graph.html") + GraphVisualization.outputGraphVisToFile(callGraph, visFile) + ctx.log("Created call graph visualization: " + visFile.getAbsoluteFile) + } + + def loadCompilationUnits(clsd: ClassDenotation): List[CompilationUnit] = clsd.dottyUnpickler match { + case Some(unpickler: DottyUnpickler) => + ctx.log("Loading compilation unit for: " + clsd) + List(FromTasty.compilationUnit(clsd, unpickler)) + case _ => Nil + } + + val topLevelClasses0 = callGraph.reachableClassesSet.map(x => x.topLevelClass.denot.asClass) + val topLevelClasses1 = topLevelClasses0.filter(x => !x.is(JavaDefined) && (x.symbol ne defn.ObjectClass)) + val newUnits = topLevelClasses1.flatMap(loadCompilationUnits) + units ::: newUnits.toList + } + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/CallGraphChecks.scala b/compiler/src/dotty/tools/dotc/transform/linker/CallGraphChecks.scala new file mode 100644 index 000000000000..2d0affbd1969 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/CallGraphChecks.scala @@ -0,0 +1,87 @@ +package dotty.tools.dotc +package transform +package linker + +import scala.language.postfixOps +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees +import dotty.tools.dotc.core.Constants._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.transform.TreeTransforms._ +import dotty.tools.dotc.transform.linker.callgraph.CallGraph + +object CallGraphChecks { + def isPhaseRequired(implicit ctx: Context): Boolean = ctx.settings.YlinkDCEChecks.value +} + +class CallGraphChecks extends MiniPhaseTransform { + import tpd._ + + def phaseName: String = "dce" + + private var doYChecks: Boolean = _ + private var callGraph: CallGraph = _ + private var assertReachableAnnotation: ClassSymbol = _ + private var assertNotReachableAnnotation: ClassSymbol = _ + private var callGraphBoundsAnnotation: ClassSymbol = _ + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { + if (DeadCodeElimination.isPhaseRequired) { + val buildCallGraphPhase = ctx.phaseOfClass(classOf[BuildCallGraph]).asInstanceOf[BuildCallGraph] + callGraph = buildCallGraphPhase.getCallGraph + assertReachableAnnotation = ctx.requiredClassRef("scala.annotation.internal.link.AssertReachable").symbol.asClass + assertNotReachableAnnotation = ctx.requiredClassRef("scala.annotation.internal.link.AssertNotReachable").symbol.asClass + callGraphBoundsAnnotation = ctx.requiredClassRef("scala.annotation.internal.link.CallGraphBounds").symbol.asClass + doYChecks = true + } else { + doYChecks = false + } + this + } + + override def transformUnit(tree: tpd.Tree)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + callGraph = null + assertReachableAnnotation = null + assertNotReachableAnnotation = null + callGraphBoundsAnnotation = null + tree + } + + override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (doYChecks) { + val sym = tree.symbol + val isReachableThroughCallGraph = callGraph.isReachableMethod(sym) + + if (isReachableThroughCallGraph && sym.hasAnnotation(assertNotReachableAnnotation)) + ctx.error("@internal.link.AssertNotReachable annotation was used on a reachable member", tree.pos) + else if (!isReachableThroughCallGraph && sym.hasAnnotation(assertReachableAnnotation)) + ctx.error("@internal.link.AssertReachable annotation was used on a non reachable member", tree.pos) + + val info = callGraph.getInfo + sym.getAnnotation(callGraphBoundsAnnotation).foreach { ann => + def check(indexInAnnotation: Int, name: String, actual: Int): Unit = { + ann.argument(indexInAnnotation) match { + case Some(lit @ Trees.Literal(Constant(bound: Int))) => + if (bound <= 0) + ctx.error(s"Invalid bound $name: bound must be positive ", lit.pos) + else if (actual > bound) + ctx.error(s"Too many $name: expected at most $bound but was $actual", lit.pos) + // else if (actual < bound / 1.2) + // ctx.error(s"Bound is not tight for $name: bound is $bound and actually have $actual", lit.pos) + case Some(arg) => ctx.error("Argument must be a literal integer", arg.pos) + case _ => assert(false) + } + } + + check(0, "reachable classes", info.reachableClasses.size) + check(1, "classes with reachable methods", info.classesWithReachableMethods.size) + check(2, "reachable methods", info.reachableDefs.size) + } + } + + tree + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/CollectSummaries.scala b/compiler/src/dotty/tools/dotc/transform/linker/CollectSummaries.scala new file mode 100644 index 000000000000..ba8eb1b1211b --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/CollectSummaries.scala @@ -0,0 +1,432 @@ +package dotty.tools.dotc.transform.linker + +import dotty.tools.dotc.FromTasty.TASTYCompilationUnit +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Names._ +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.SymDenotations.ClassDenotation +import dotty.tools.dotc.core.tasty._ +import dotty.tools.dotc.core.tasty.DottyUnpickler +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.transform.TreeGen +import dotty.tools.dotc.transform.TreeTransforms._ +import dotty.tools.dotc.transform.linker.summaries._ +import dotty.tools.dotc.transform.linker.types.{ClosureType, PreciseType} +import dotty.tools.dotc.typer.Applications +import dotty.tools.dotc.typer.Applications._ + +import scala.annotation.tailrec +import scala.collection.mutable + +class CollectSummaries extends MiniPhase { thisTransform => + import tpd._ + + /** the following two members override abstract members in Transform */ + val phaseName: String = "summaries" + + val treeTransform: Collect = new Collect + + override def run(implicit ctx: Context): Unit = { + if (CollectSummaries.isPhaseRequired) + super.run + } + + def methodSummaries: Map[Symbol, MethodSummary] = treeTransform.getMethodSummaries + + class Collect extends TreeTransform { + def phase: CollectSummaries = thisTransform + + private var methodSummaries: Map[Symbol, MethodSummary] = Map.empty + private var methodSummaryStack: mutable.Stack[MethodSummaryBuilder] = mutable.Stack() + private var curMethodSummary: MethodSummaryBuilder = _ + + def getMethodSummaries: Map[Symbol, MethodSummary] = methodSummaries + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { + if (ctx.compilationUnit.isInstanceOf[TASTYCompilationUnit]) + NoTransform // will retrieve them lazily + else this + } + + override def prepareForDefDef(tree: tpd.DefDef)(implicit ctx: Context): TreeTransform = { + val sym = tree.symbol + if (!sym.is(Label) && !sym.isPrimaryConstructor) { + methodSummaryStack.push(curMethodSummary) + val args = tree.vparamss.flatten.map(_.symbol) // outer param for constructors + val argumentStoredToHeap = (0 to args.length).map(_ => true).toList + curMethodSummary = new MethodSummaryBuilder(sym, argumentStoredToHeap) + } + this + } + + override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + if (!tree.symbol.is(Label) && !tree.symbol.isPrimaryConstructor) { + assert(curMethodSummary.methodDef eq tree.symbol) + assert(!methodSummaries.contains(curMethodSummary.methodDef)) + methodSummaries = methodSummaries.updated(curMethodSummary.methodDef, curMethodSummary.result()) + curMethodSummary = methodSummaryStack.pop() + } + tree + } + + override def prepareForValDef(tree: tpd.ValDef)(implicit ctx: Context): TreeTransform = { + val sym = tree.symbol + if (sym.exists && ((sym.is(Lazy) && (sym.owner.is(Package) || sym.owner.isClass)) || //lazy vals and modules + sym.owner.name.startsWith(StdNames.str.LOCALDUMMY_PREFIX) || // blocks inside constructor + sym.owner.isClass)) { // fields + // owner is a template + methodSummaryStack.push(curMethodSummary) + curMethodSummary = new MethodSummaryBuilder(sym, List(true)) + } + this + } + + override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + val sym = tree.symbol + if (sym.exists) { + val ownerIsClass = sym.owner.isClass + val isLazyValOrModule = sym.is(Lazy) && (ownerIsClass || sym.owner.is(Package)) + val isBockInsideConstructor = sym.owner.name.startsWith(StdNames.str.LOCALDUMMY_PREFIX) + if (isLazyValOrModule || isBockInsideConstructor || ownerIsClass) { + assert(curMethodSummary.methodDef eq tree.symbol) + assert(!methodSummaries.contains(curMethodSummary.methodDef)) + methodSummaries = methodSummaries.updated(curMethodSummary.methodDef, curMethodSummary.result()) + curMethodSummary = methodSummaryStack.pop() + } + if (!isLazyValOrModule && (isBockInsideConstructor || ownerIsClass)) + registerCall(tree) + } + tree + } + + override def prepareForTemplate(tree: tpd.Template)(implicit ctx: Context): TreeTransform = { + val sym = tree.symbol + assert(!sym.is(Label)) + methodSummaryStack.push(curMethodSummary) + curMethodSummary = new MethodSummaryBuilder(sym.owner.primaryConstructor, List(true)) + this + } + + override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + val sym = tree.symbol + assert(!sym.is(Label)) + assert(curMethodSummary.methodDef eq tree.symbol.owner.primaryConstructor) + assert(!methodSummaries.contains(curMethodSummary.methodDef)) + methodSummaries = methodSummaries.updated(curMethodSummary.methodDef, curMethodSummary.result()) + curMethodSummary = methodSummaryStack.pop() + tree + } + + /* + override def transformTypeDef(tree: tpd.TypeDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + val sym = tree.symbol + if (sym.isClass) { + val isEntryPoint = dotty.tools.backend.jvm.CollectEntryPoints.isJavaEntryPoint(sym) + /*summaries = ClassSummary(sym.asClass, + methodSummaries + ) :: summaries + methodSummaries = Nil*/ + } + tree + } + */ + + def registerModule(sym: Symbol)(implicit ctx: Context): Unit = { + if ((curMethodSummary ne null) && sym.is(ModuleVal)) { + curMethodSummary.addAccessedModules(sym) + registerModule(sym.owner) + } + val res = sym.info.finalResultType.termSymbol + if ((curMethodSummary ne null) && res.is(ModuleVal)) { + curMethodSummary.addAccessedModules(res) + registerModule(res.owner) + } + + } + + def registerCall(tree: Tree)(implicit ctx: Context): Unit = { + + def symbolOf(t: Tree) = { + val s = t.symbol.orElse(t.tpe.classSymbol).orElse(TypeErasure.erasure(t.tpe).classSymbol) + assert(s.exists) + s + } + + @tailrec def receiverArgumentsAndSymbol(t: Tree, accArgs: List[List[Tree]] = Nil, accT: List[Tree] = Nil): + (Tree, Tree, List[List[Tree]], List[Tree], TermRef) = t match { + case Block(stats, expr) => receiverArgumentsAndSymbol(expr, accArgs, accT) + case TypeApply(fun, targs) if fun.symbol eq t.symbol => receiverArgumentsAndSymbol(fun, accArgs, targs) + case Apply(fn, args) if fn.symbol == t.symbol => receiverArgumentsAndSymbol(fn, args :: accArgs, accT) + case Select(qual, _) => + (qual, t, accArgs, accT, t.tpe.asInstanceOf[TermRef]) + case x: This => (x, x, accArgs, accT, x.tpe.asInstanceOf[TermRef]) + case x => (x, x, accArgs, accT, x.tpe.asInstanceOf[TermRef]) + } + val widenedTp = tree.tpe.widen + if (!widenedTp.isInstanceOf[MethodicType] || (tree.symbol.exists && !tree.symbol.info.isInstanceOf[MethodicType])) { + val (receiver, _ /*call*/ , arguments, typeArguments, method) = receiverArgumentsAndSymbol(tree) + + val storedReceiver = receiver.tpe + + assert(storedReceiver.exists) + + def wrapArrayTermRef(wrapArrayMethodName: TermName) = + TermRef(defn.ScalaPredefModuleRef, defn.ScalaPredefModule.requiredMethod(wrapArrayMethodName)) + + @tailrec def skipBlocks(s: Tree): Tree = s match { + case s: Block => skipBlocks(s.expr) + case _ => s + } + + @tailrec def argType(x: Tree): Type = skipBlocks(x) match { + case exp: Closure => + val SAMType(e) = exp.tpe + new ClosureType(exp, x.tpe, e.symbol) + case Select(New(tp), _) => new PreciseType(tp.tpe) + case Apply(Select(New(tp), _), args) => new PreciseType(tp.tpe) + case Apply(TypeApply(Select(New(tp), _), targs), args) => new PreciseType(tp.tpe) + case Typed(expr: SeqLiteral, tpt) if x.tpe.isRepeatedParam => + val tp = expr.elemtpt.tpe + wrapArrayTermRef(TreeGen.wrapArrayMethodName(tp)).widenDealias match { + case warr: PolyType => warr.appliedTo(tp).finalResultType + case warr => warr.finalResultType + } + case Typed(expr, _) => argType(expr) + case NamedArg(nm, a) => argType(a) + case _ => x.tpe + } + + val thisCallInfo = CallInfo(method, typeArguments.map(_.tpe), arguments.flatten.map(argType)) + lazy val someThisCallInfo = Some(thisCallInfo) + + // Create calls to wrapXArray for varArgs + val repeatedArgsCalls = tree match { + case Apply(fun, _) if fun.symbol.info.isVarArgsMethod => + @tailrec def refine(tp: Type): Type = tp match { + case tp: TypeAlias => refine(tp.alias.dealias) + case tp: RefinedType if tp.parent == defn.RepeatedParamType => refine(tp.refinedInfo) + case tp: TypeBounds => refine(tp.hi) + case _ => tp + } + @tailrec def getVarArgTypes(tp: Type, acc: List[Type] = Nil): List[Type] = tp match { + case tp: PolyType => getVarArgTypes(tp.resultType, acc) + case tp: MethodType => + val paramInfos = tp.paramInfos + lazy val lastParamType = paramInfos.last + if (paramInfos.isEmpty || !lastParamType.isRepeatedParam) acc + else getVarArgTypes(tp.resultType, refine(lastParamType) :: acc) + case _ => acc + } + + getVarArgTypes(fun.tpe.widenDealias).map { tp => + val wrapArrayName = TreeGen.wrapArrayMethodName(tp) + val targs = if (wrapArrayName == nme.wrapRefArray || wrapArrayName == nme.genericWrapArray) List(tp) else Nil + val args = List(defn.ArrayOf(tp)) + CallInfo(wrapArrayTermRef(wrapArrayName), targs, args, someThisCallInfo) + } + + case _ => Nil + } + + val isInPredef = + ctx.owner.ownersIterator.exists(owner => owner == defn.ScalaPredefModule || owner.companionModule == defn.ScalaPredefModule) + + val isMethodOnPredef = { + val funTpe = tree match { + case Apply(TypeApply(fun: Ident, _), _) => fun.tpe + case Apply(fun: Ident, _) => fun.tpe + case _ => tree.tpe + } + funTpe.normalizedPrefix == defn.ScalaPredefModuleRef + } + + val loadPredefModule = if (!isInPredef && (repeatedArgsCalls.nonEmpty || isMethodOnPredef)) { + List(CallInfo(defn.ScalaPredefModuleRef, Nil, Nil, someThisCallInfo)) + } else { + Nil + } + + val sym = tree.symbol + val mixinConstructors: List[CallInfo] = { + if (!sym.isPrimaryConstructor) { + Nil + } else { + val directMixins = sym.owner.mixins.diff(sym.owner.info.parents.head.symbol.mixins) + directMixins.collect { + case mixin if !mixin.is(NoInits) => + val decl = mixin.primaryConstructor + val (tparams, params) = decl.info match { + case tp: PolyType => (mixin.info.typeParams.map(_.paramRef), tp.resType.paramInfoss.flatten) + case tp => (Nil, tp.paramInfoss.flatten) + } + CallInfo(decl.termRef, tparams, params, someThisCallInfo) + } + } + } + + val languageDefinedCalls = loadPredefModule ::: mixinConstructors ::: repeatedArgsCalls + + curMethodSummary.addMethodsCalledBy(storedReceiver, thisCallInfo :: languageDefinedCalls) + } + } + + override def transformIdent(tree: tpd.Ident)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + if (!tree.symbol.is(Package)) { + registerModule(tree.symbol) + } + val select = tree.tpe match { + case TermRef(prefix: TermRef, name) => + Some(tpd.ref(prefix).select(tree.symbol)) + case TermRef(prefix: ThisType, name) => + Some(tpd.This(prefix.cls).select(tree.symbol)) + case TermRef(NoPrefix, name) => + if (tree.symbol is Method | Lazy) { // todo: this kills dotty { + val widenedTp = tree.tpe.widen + if (widenedTp.isInstanceOf[MethodicType] && (!tree.symbol.exists || tree.symbol.info.isInstanceOf[MethodicType])) + return tree + registerCall(tree) + return tree + // Some(This(tree.symbol.topLevelClass.asClass).select(tree.symbol)) // workaround #342 todo: remove after fixed + } + else None + case _ => None + } + + select.map(transformSelect) + + tree + } + + override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + val sym = tree.symbol + if (!sym.is(Package | Label) && !sym.isClass && !sym.isType) { + registerModule(sym) + registerCall(tree) + } + // handle nullary methods + tree + } + + override def transformThis(tree: tpd.This)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + curMethodSummary.setThisAccessed(true) + tree + } + + override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + if (!tree.symbol.is(Label)) + registerCall(tree) + tree + } + + override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + registerCall(tree) + tree + } + + def registerUnApply(selector: tpd.Tree, tree: tpd.UnApply)(implicit ctx: Context, info: TransformerInfo): Unit = { + def registerNestedUnapply(nestedSelector: Tree, nestedPattern: Tree): Unit = nestedPattern match { + case nestedUnapply: UnApply => registerUnApply(nestedSelector, nestedUnapply) + case _ => + } + + def registerNestedUnapplyFromProduct(product: Tree, patterns: List[Tree]): Unit = + for ((nestedPat, idx) <- patterns.zipWithIndex) { + val nestedSel = product.select(nme.selectorName(idx)) + registerCall(nestedSel) // register call to Product._x + registerNestedUnapply(nestedSel, nestedPat) + } + + def registerNestedUnapplyFromSeq(seq: Tree, patterns: List[Tree]): Unit = { + registerCall(seq.select(nme.lengthCompare).appliedTo(Literal(Constant(patterns.size)))) + + if (patterns.size >= 1) { + val headSel = seq.select(nme.head) + val tailSels = for (i <- 1 until patterns.size) yield seq.select(nme.apply).appliedTo(Literal(Constant(i))) + val nestedSels = Seq(headSel) ++ tailSels + + for ((nestedSel, nestedPat) <- nestedSels zip patterns) { + registerCall(nestedSel) + registerNestedUnapply(nestedSel, nestedPat) + } + } + } + + val unapplyCall = Apply(tree.fun, List(selector)) + registerCall(unapplyCall) + + val unapplyResultType = unapplyCall.tpe + val hasIsDefined = extractorMemberType(unapplyResultType, nme.isEmpty) isRef defn.BooleanClass + val hasGet = extractorMemberType(unapplyResultType, nme.get).exists + + if (hasIsDefined && hasGet) { // if result of unapply is an Option + val getCall = unapplyCall.select(nme.get) + + // register Option.isDefined and Option.get calls + registerCall(unapplyCall.select(nme.isEmpty)) + registerCall(getCall) + + if (tree.fun.symbol.name == nme.unapplySeq) // result of unapplySeq is Option[Seq[T]] + registerNestedUnapplyFromSeq(getCall, tree.patterns) + else if (tree.patterns.size == 1) // result of unapply is Option[T] + registerNestedUnapply(getCall, tree.patterns.head) + else // result of unapply is Option[(T1, ..., Tn)] + registerNestedUnapplyFromProduct(getCall, tree.patterns) + + } else if (defn.isProductSubType(unapplyResultType)) { + // if result of unapply is a Product + registerNestedUnapplyFromProduct(unapplyCall, tree.patterns) + } + } + + def collectMatch(selector: tpd.Tree, cases: List[tpd.CaseDef])(implicit ctx: Context, info: TransformerInfo): Unit = { + cases foreach { case CaseDef(pat, _, _) => pat match { + case unapply: tpd.UnApply => registerUnApply(selector, unapply) + case _ => + }} + } + + override def transformMatch(tree: tpd.Match)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + collectMatch(tree.selector, tree.cases) + + tree + } + + override def transformTry(tree: tpd.Try)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + // generate synthetic selector of Throwable type (from TryCatchPatterns.scala) + val exName = NameKinds.DefaultExceptionName.fresh() + val fallbackSelector = ctx.newSymbol(ctx.owner, exName, Flags.Synthetic | Flags.Case, defn.ThrowableType) + val sel = Ident(fallbackSelector.termRef) + + collectMatch(sel, tree.cases) + + tree + } + + + override def transformClosure(tree: tpd.Closure)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + if (curMethodSummary ne null) { + curMethodSummary.addDefinedClosure(new ClosureType(tree, tree.tpe, tree.meth.symbol)) + } + tree + } + + override def transformUnit(tree: tpd.Tree)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + TastySummaries.saveInTasty(methodSummaries.values.toList) + tree + } + } +} + +object CollectSummaries { + + def isPhaseRequired(implicit ctx: Context): Boolean = BuildCallGraph.isPhaseRequired + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/DeadCodeElimination.scala b/compiler/src/dotty/tools/dotc/transform/linker/DeadCodeElimination.scala new file mode 100644 index 000000000000..ae2e7f2aebae --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/DeadCodeElimination.scala @@ -0,0 +1,93 @@ +package dotty.tools.dotc +package transform +package linker + +import scala.language.postfixOps +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.transform.TreeTransforms._ +import dotty.tools.dotc.transform.linker.callgraph.CallGraph + +object DeadCodeElimination { + def isPhaseRequired(implicit ctx: Context): Boolean = + ctx.settings.linkDCE.value || ctx.settings.linkDCEAggressive.value +} + +class DeadCodeElimination extends MiniPhaseTransform { + import tpd._ + + def phaseName: String = "dce" + + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[CallGraphChecks]) + + private var doTransform: Boolean = _ + private var callGraph: CallGraph = _ + private var buildCallGraphPhase: BuildCallGraph = _ + private var exception: Tree = _ + private var doNotDCEAnnotation: ClassSymbol = _ + private var aggressive: Boolean = _ + + // TODO: Should this set be removed after Ycheck? + private var aggressivelyDCEd: Set[Symbol] = _ + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { + if (DeadCodeElimination.isPhaseRequired) { + buildCallGraphPhase = ctx.phaseOfClass(classOf[BuildCallGraph]).asInstanceOf[BuildCallGraph] + callGraph = buildCallGraphPhase.getCallGraph + exception = Throw(New(ctx.requiredClassRef("dotty.runtime.DeadCodeEliminated"), Nil)) + doNotDCEAnnotation = ctx.requiredClassRef("scala.annotation.internal.link.DoNotDeadCodeEliminate").symbol.asClass + aggressive = ctx.settings.linkDCEAggressive.value + doTransform = true + } else { + doTransform = false + } + this + } + + override def transformUnit(tree: tpd.Tree)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + buildCallGraphPhase = null + callGraph = null + exception = null + doNotDCEAnnotation = null + doTransform = false + tree + } + + override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + if (!doTransform || doNotEliminate(sym)) tree + else if (aggressive && !doNotEliminateAggressive(sym)) { + if (aggressivelyDCEd eq null) + aggressivelyDCEd = Set.empty + aggressivelyDCEd += sym + EmptyTree + } else tpd.cpy.DefDef(tree)(rhs = exception) + } + + def wasAggressivelyDCEd(x: Symbol): Boolean = { + aggressivelyDCEd != null && aggressivelyDCEd.contains(x) + } + + private def doNotEliminate(sym: Symbol)(implicit ctx: Context): Boolean = { + callGraph.isReachableMethod(sym) || sym.is(Label) || sym.isConstructor || keepAsNew(sym) || + (sym.isSetter && callGraph.isReachableMethod(sym.getter)) || sym.hasAnnotation(doNotDCEAnnotation) + } + + private def doNotEliminateAggressive(sym: Symbol)(implicit ctx: Context): Boolean = { + sym.owner.fullName.startsWith("scala.Predef") || + defn.ArrayMethods.contains(sym) || + defn.DottyArraysMethods.contains(sym) || + (sym.is(Deferred) && sym.owner.is(Abstract)) || + sym.owner.is(Lazy | Method) || + sym.is(Implicit) || sym.is(Synthetic) || sym.owner.is(Trait) + } + + private def keepAsNew(sym: Symbol)(implicit ctx: Context): Boolean = + sym.initial.validFor.firstPhaseId > buildCallGraphPhase.period.phaseId +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraph.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraph.scala new file mode 100644 index 000000000000..6cb5dce9f290 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraph.scala @@ -0,0 +1,84 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ + +import scala.collection.mutable + +class CallGraph(val entryPoints: Map[CallInfoWithContext, Int], val reachableMethods: Set[CallInfoWithContext], + val reachableTypes: Set[TypeWithContext], val casts: Set[Cast], val classOfs: Set[Symbol], + val outerMethods: Set[Symbol], val mode: Int, val specLimit: Int)(implicit ctx: Context) { self => + + private val reachableMethodSet: Set[Symbol] = + reachableMethods.map(_.callSymbol) + + lazy val reachableClassesSet: Set[Symbol] = { + val reachableClasses = mutable.Set.empty[Symbol] + def collectClasses(cls: ClassSymbol): Unit = { + reachableClasses.add(cls) + cls.classParents.foreach(x => collectClasses(x.symbol.asClass)) + } + reachableTypes.foreach(x => x.tp.classSymbols.foreach(collectClasses)) + reachableClasses.toSet + } + + def isReachableMethod(sym: Symbol): Boolean = reachableMethodSet.contains(sym) + + def isReachableClass(sym: Symbol): Boolean = reachableClassesSet.contains(sym) + + def isReachableClassOf(sym: Symbol): Boolean = classOfs.contains(sym) + + def getInfo(): Info = new Info + + class Info { + + lazy val classesWithReachableMethods = reachableMethodSet.map(_.maybeOwner.info.widen.classSymbol) + + lazy val reachableClasses = classesWithReachableMethods ++ reachableClassesSet + + lazy val reachableDefs = reachableMethods.map(_.callSymbol) + + lazy val reachableSpecs: Set[(Symbol, List[Type])] = reachableMethods.flatMap { x => + val clas = x.callSymbol.maybeOwner.info.widen.classSymbol + val meth = x.callSymbol + if (mode >= CallGraphBuilder.AnalyseTypes) (meth, x.call.normalizedPrefix.baseArgInfos(clas)) :: Nil + else { + val clazSpecializationsCount = + if (clas.primaryConstructor.info.widenDealias.isInstanceOf[PolyType]) specLimit + else 1 + val methodSpecializationsCount = + if (meth.info.widenDealias.isInstanceOf[PolyType]) specLimit + else 1 + ctx.log(s"specializing $clas $meth for $clazSpecializationsCount * $methodSpecializationsCount") + (0 until clazSpecializationsCount*methodSpecializationsCount).map(x => (meth, ConstantType(Constant(x)):: Nil)).toList + } + } + + private lazy val morphisms = reachableMethods.groupBy(x => x.callee).groupBy(x => x._2.map(_.callSymbol).toSet.size) + + lazy val monomorphicCalls = if (morphisms.contains(1)) morphisms(1) else Map.empty + + lazy val bimorphicCalls = if (morphisms.contains(2)) morphisms(2) else Map.empty + + lazy val megamorphicCalls = morphisms - 1 - 2 + + def log()(implicit ctx: Context): Unit = { + ctx.log( + s""" + |Call graph info: + | Found: ${classesWithReachableMethods.size} classes with reachable methods, ${reachableClasses.size} reachable classes, ${reachableDefs.size} reachable methods, ${reachableSpecs.size} specializations + | mono: ${monomorphicCalls.size}, bi: ${bimorphicCalls.size}, mega: ${megamorphicCalls.map(_._2.size).sum} + | Found ${outerMethods.size} not defined calls: ${outerMethods.map(_.showFullName)} + | Reachable classes: ${reachableClasses.map(_.showFullName).mkString(", ")} + | Reachable methods: ${reachableDefs.map(_.showFullName).mkString(", ")} + | Classes with reachable methods: ${classesWithReachableMethods.map(_.showFullName).mkString(", ")} + | Reachable specs: ${reachableSpecs.toList.filter(_._2.nonEmpty).sortBy(-_._2.size).map(x => (x._1.showFullName, x._2.map(_.show))).mkString(", ")} + | Primary Constructor specs: ${reachableSpecs.filter(x => x._1.isPrimaryConstructor && x._2.nonEmpty).map(x => (x._1.showFullName, x._2))} + """.stripMargin + ) + } + + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala new file mode 100644 index 000000000000..80fdfbae31c3 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala @@ -0,0 +1,723 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Names._ +import dotty.tools.dotc.core.NameKinds._ +import dotty.tools.dotc.core.NameOps._ +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols.{Symbol, _} +import dotty.tools.dotc.core.TypeErasure +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.ResolveSuper +import dotty.tools.dotc.transform.linker.summaries._ +import dotty.tools.dotc.transform.linker.types._ + +import scala.annotation.tailrec +import scala.collection.{immutable, mutable} + +object CallGraphBuilder { + final val AnalyseOrig = 1 + final val AnalyseTypes = 2 + final val AnalyseArgs = 3 +} + +class CallGraphBuilder(collectedSummaries: Map[Symbol, MethodSummary], mode: Int, specLimit: Int, + withJavaCallGraph: Boolean, withOutEdges: Boolean)(implicit ctx: Context) { + import OuterTargs.parentRefinements + import CallGraphBuilder._ + import tpd._ + + private val tastySummaries = new TastySummaries + + private var iteration = 0 + + private var entryPoints = immutable.Map.empty[CallInfoWithContext, Int] + private var reachableTypes = immutable.Set.empty[TypeWithContext] + private var reachableMethods = mutable.Set.empty[CallInfoWithContext] + + private val reachableMethodsSymbols = new java.util.IdentityHashMap[Symbol, Unit] + private val outerMethods = new java.util.IdentityHashMap[Symbol, Unit] + + private val classOfs = new java.util.IdentityHashMap[Symbol, Unit] + + private val typesByMemberNameCache = new java.util.IdentityHashMap[Name, List[(Int, TypeWithContext)]]() + + private var casts = immutable.Set.empty[Cast] + private val castsCache: mutable.HashMap[TypeWithContext, mutable.Set[Cast]] = mutable.HashMap.empty + private val castCache1 = new java.util.IdentityHashMap[ClassSymbol, Set[Cast]] + + private val sizesCache = new java.util.IdentityHashMap[Symbol, Int]() + private val lastInstantiation = mutable.Map.empty[CallInfoWithContext, mutable.Map[CallInfo, Int]] + + private var finished = false // TODO: once the crc is complete, remove finished and use addingOutEdges instead. + private var addingOutEdges = false + + private val normalizeType: Type => Type = new TypeNormalizer + + def pushEntryPoint(s: Symbol, entryPointId: Int): Unit = { + val tpe = ref(s).tpe.asInstanceOf[TermRef] + val targsSize = tpe.widen match { + case t: PolyType => t.paramNames.size + case _ => 0 + } + val targs = (0 until targsSize).map(x => new ErazedType()).toList + val args = tpe.widenDealias.paramInfoss.flatten + val call = CallInfoWithContext(tpe, targs, args, OuterTargs.empty, None, None) + entryPoints = entryPoints.updated(call, entryPointId) + addReachableMethod(call) + val t = normalizeType(ref(s.owner).tpe) + val self = new TypeWithContext(t, parentRefinements(t)) + addReachableType(self) + } + + /** Builds the call graph based on the current reachable items, mainly entry points. */ + def build(): Unit = { + ctx.log(s"Building call graph with ${entryPoints.values.toSet.size} entry points") + val startTime = System.currentTimeMillis() + @tailrec def buildLoop(): Unit = { + val loopStartTime = System.currentTimeMillis() + + val reachableMethodsLastSize = reachableMethods.size + val reachableTypesLastSize = reachableTypes.size + val castsLastSize = casts.size + val classOfsLastSize = classOfs.size + + processCallSites() + + val numNewReachableMethods = reachableMethods.size - reachableMethodsLastSize + val numNewReachableTypes = reachableTypes.size - reachableTypesLastSize + val numNewCasts = casts.size - castsLastSize + val numNewClassOfs = classOfs.size - classOfsLastSize + + iteration += 1 + val loopEndTime = System.currentTimeMillis() + val loopTime = loopEndTime - loopStartTime + val totalTime = loopEndTime - startTime + val msgInfo = { + if (addingOutEdges) "" + else + s""" + |Found $numNewReachableTypes new instantiated types (${reachableTypes.size}) + |Found $numNewReachableMethods new call sites (${reachableMethods.size}) + |Found $numNewCasts new casts (${casts.size}) + |Found $numNewClassOfs new classOfs (${classOfs.size}) + """.stripMargin + } + ctx.log( + s"""Graph building iteration $iteration + |Iteration in ${loopTime/1000.0} seconds out of ${totalTime/1000.0} seconds (${loopTime.toDouble/totalTime}) + |$msgInfo""".stripMargin) + + // val outFile = new java.io.File(ctx.settings.d.value + s"/CallGraph-${reachableMethods.items.size}.html") + // GraphVisualization.outputGraphVisToFile(this.result(), outFile) + + if (numNewReachableMethods != 0 || numNewReachableTypes != 0 || numNewCasts != 0) + buildLoop() + else if (!finished) { + // This last loop is only here to check the correctness on the crc map + // TODO: remove in production mode + ctx.log("[processing all call sites for crc check]") + finished = true + buildLoop() + } else if (withOutEdges && !addingOutEdges) { + ctx.log("[processing all call sites for to add out edges]") + addingOutEdges = true + buildLoop() + } + } + + buildLoop() + } + + /** Packages the current call graph into a CallGraph */ + def result(): CallGraph = { + import scala.collection.JavaConversions._ + val entryPoints = this.entryPoints + val reachableMethods = this.reachableMethods.toSet + val reachableTypes = this.reachableTypes + val casts = this.casts + val classOfs = this.classOfs.keySet().toSet + val outerMethods = this.outerMethods.keySet().toSet + new CallGraph(entryPoints, reachableMethods, reachableTypes, casts, classOfs, outerMethods, mode, specLimit) + } + + private def registerTypeForSize(tp: Type): Unit = { + val classSymbols = mutable.Set.empty[Symbol] + def collectClasses(cls: ClassSymbol): Unit = { + classSymbols.add(cls) + cls.classParents.foreach(x => collectClasses(x.symbol.asClass)) + } + tp.classSymbols.foreach(collectClasses) + classSymbols.foreach(sym => sizesCache.put(sym, sizesCache.getOrDefault(sym, 0) + 1)) + } + + private def addReachableType(x: TypeWithContext): Unit = { + if (!reachableTypes.contains(x)) { + assert(!finished) + + registerTypeForSize(new SubstituteByParentMap(x.outerTargs).apply(x.tp)) + + reachableTypes += x + + lazy val deepness = TypeDepth(x.tp) + val namesInType = x.tp.memberNames(takeAllFilter).filter(typesByMemberNameCache.containsKey) + for (name <- namesInType) { + typesByMemberNameCache.put(name, (deepness, x) :: typesByMemberNameCache.get(name)) + } + + val clas = x.tp match { + case t: ClosureType => t.u.classSymbol.asClass + case t: JavaAllocatedType => t.underlying.widenDealias.classSymbol.asClass + case _ => x.tp.classSymbol.asClass + } + if (!clas.is(JavaDefined) && clas.is(Module)) { + val fields = clas.classInfo.decls.filter(x => !x.is(Method) && !x.isType) + val parent = Some(CallInfoWithContext(x.tp.select(clas.primaryConstructor).asInstanceOf[TermRef], x.tp.baseArgInfos(clas), Nil, x.outerTargs, None, None)) + fields.foreach { fieldSym => + addReachableMethod(CallInfoWithContext(x.tp.select(fieldSym).asInstanceOf[TermRef], Nil, Nil, x.outerTargs, parent, None)) + } + } + } + } + + private def addReachableMethod(method: CallInfoWithContext): Unit = { + if (!reachableMethods.contains(method)) { + finished = false // TODO: replace with assert(!finished) + reachableMethods += method + val callSymbol = method.callSymbol + if (!reachableMethodsSymbols.containsKey(callSymbol)) { + reachableMethodsSymbols.put(callSymbol, ()) + collectedSummaries.get(callSymbol).foreach { summary => + summary.accessedModules.foreach(x => addReachableType(new TypeWithContext(normalizeType(x.info), parentRefinements(x.info)))) + } + } + } + } + + private def addReachableClosure(x: ClosureType, from: CallInfoWithContext): Unit = { + val substitution = new SubstituteByParentMap(from.outerTargs) + val tp = substitution(x) + addReachableType(new TypeWithContext(tp, parentRefinements(tp))) + } + + private def getTypesByMemberName(x: Name): List[(Int, TypeWithContext)] = { + val ret1 = typesByMemberNameCache.get(x) + if (ret1 eq null) { + // not yet computed + val upd = reachableTypes.iterator.collect { case tp if tp.tp.member(x).exists => (TypeDepth(tp.tp), tp) }.toList + typesByMemberNameCache.put(x, upd) + upd + } else ret1 + } + + private def addCast(from: Type, to: Type) = { + if (!(from <:< to) && to.classSymbols.forall(!_.derivesFrom(defn.NothingClass))) { + val newCast = new Cast(from, to) + var addedCast = false + val classSymbols = from.classSymbols.toSet ++ to.classSymbols + for (tp <- reachableTypes) { + if (classSymbols.forall(x => tp.tp.classSymbols.exists(y => y.derivesFrom(x)))) { + val cached = castsCache.getOrElseUpdate(tp, mutable.Set.empty) + if (!cached.contains(newCast)) { + if (!addedCast) { + casts += newCast + + def collectClasses(cls: ClassSymbol): Unit = { + castCache1.put(cls, castCache1.getOrDefault(cls, Set.empty) + newCast) + cls.classParents.foreach(x => collectClasses(x.symbol.asClass)) + } + to.classSymbols.foreach(collectClasses) + + addedCast = true + } + finished = false // TODO: replace with assert(!finished) + cached += newCast + } + } + } + } + } + + private val registeredParentModules = mutable.Set.empty[Type] + private def registerParentModules(tp: Type): Unit = { + if ((tp ne NoType) && (tp ne NoPrefix) && !registeredParentModules.contains(tp)) { + if (tp.widen ne tp) registerParentModules(tp.widen) + if (tp.dealias ne tp) registerParentModules(tp.dealias) + if (tp.termSymbol.is(Module)) { + addReachableType(new TypeWithContext(tp.widenDealias, parentRefinements(tp.widenDealias))) + } else if (tp.typeSymbol.is(Module, butNot = Package)) { + val t = normalizeType(ref(tp.typeSymbol).tpe) + addReachableType(new TypeWithContext(t, parentRefinements(t))) + } + registeredParentModules += tp + registerParentModules(tp.normalizedPrefix): @tailrec + } + } + + private def instantiateCallSite(caller: CallInfoWithContext, callee: CallInfo, registerCall: CallInfoWithContext => Boolean): Unit = { + + def addCall(call: CallInfoWithContext) = { + if (registerCall(call)) { + if (addingOutEdges) caller.addOutEdges(callee, call) + else addReachableMethod(call) + } + } + + lazy val someCaller = Some(caller) + lazy val someCallee = Some(callee) + + val receiver = callee.call.normalizedPrefix + + registerParentModules(receiver) + + lazy val receiverDepth = TypeDepth(receiver) + + val calleeSymbol = callee.call.termSymbol.asTerm + val callerSymbol = caller.call.termSymbol + + val tpamsOuter = caller.call.widen match { + case t: PolyType => + OuterTargs.empty.addAll(callerSymbol, t.paramNames, caller.targs) + case _ => + OuterTargs.empty + } + + lazy val outerParent = if (callerSymbol.owner ne caller.call.normalizedPrefix.classSymbol) { + val current = caller.call.normalizedPrefix + val superTpe = callerSymbol.owner.info + @tailrec def collectTypeMembers(classes: List[ClassSymbol], acc: Map[Name, Symbol]): Iterable[Symbol] = classes match { + case x :: xs => + val typeDecls = x.unforcedDecls.filter(_.isType) + val acc2 = typeDecls.foldLeft(acc)((acc1, sym) => if (acc1.contains(sym.name)) acc1 else acc1.updated(sym.name, sym)) + collectTypeMembers(xs, acc2) + case Nil => acc.values + } + val typeMembers = collectTypeMembers(current.baseClasses, Map.empty) + val outers = typeMembers.foldLeft(OuterTargs.empty) { (outerTargs: OuterTargs, x) => + val old = superTpe.member(x.symbol.name) + if (old.exists) outerTargs.add(callerSymbol.owner, x.symbol.name, current.select(x).widen) else outerTargs + } + outers + } else OuterTargs.empty + + lazy val outerPropagatedTargs = caller.outerTargs ++ tpamsOuter ++ outerParent + lazy val substitution = new SubstituteByParentMap(outerPropagatedTargs) + + def propagateArgs(tp: Type): Type = { + tp match { + case x: TermRef if mode >= AnalyseArgs && x.symbol.is(Param) && x.symbol.owner == caller.call.termSymbol => + val id = caller.call.termSymbol.info.paramNamess.iterator.flatten.indexWhere(_ == x.symbol.name) + caller.argumentsPassed(id) + case t => t + } + } + + def propagateTargs(tp0: Type, isConstructor: Boolean = false): Type = { + val tp = propagateArgs(tp0) + val ret = + if (mode >= AnalyseTypes && (caller.targs.nonEmpty || caller.outerTargs.nonEmpty || (callerSymbol.owner ne caller.call.normalizedPrefix.classSymbol))) { + /* && tp.widenDealias.existsPart{x => val t = x.typeSymbol; t.exists && (t.owner == callerSymbol || caller.outerTargs.contains(t.owner))}*/ + + val refinedClassType = if (isConstructor) { + val refinedConstructedType = tp.typeMembers.foldLeft(tp){(acc, memberType) => + val refinedInfo = substitution.apply(memberType.info) + if (refinedInfo ne memberType.info) RefinedType(acc, memberType.symbol.name, refinedInfo) + else acc + } + refinedConstructedType + } else if (mode >= AnalyseArgs && (tp.isInstanceOf[PreciseType] || tp.isInstanceOf[ClosureType])) tp + else tp.widenDealias + val r = substitution.apply(refinedClassType) + // for simplification only + if (r =:= tp) tp + else r + } else tp + + assert(!(tp.isValueType ^ ret.isValueType)) + ret + } + + val outerTargs: OuterTargs = + if (mode < AnalyseTypes) OuterTargs.empty + else if (calleeSymbol.isProperlyContainedIn(callerSymbol)) { + parentRefinements(propagateTargs(receiver)) ++ caller.outerTargs ++ tpamsOuter + } else { + val t = parentRefinements(propagateTargs(receiver)) ++ caller.outerTargs + val (a, b) = t.mp.partition(x => calleeSymbol.isProperlyContainedIn(x._1)) + new OuterTargs(a) combine new OuterTargs(b) + } + + // if typearg of callee is a typeparam of caller, propagate typearg from caller to callee + lazy val targs = callee.targs map { + case t: TypeVar if mode >= AnalyseTypes && t.stripTypeVar.typeSymbol.maybeOwner == caller.call.termSymbol => + assert(caller.call.termSymbol.exists) + val abstractSym = t.stripTypeVar.typeSymbol + val id = caller.call.termSymbol.info.asInstanceOf[PolyType].paramNames.indexOf(abstractSym.name) + propagateTargs(caller.targs(id).stripTypeVar) + case t if mode >= AnalyseTypes => propagateTargs(t.stripTypeVar) + case t => t.stripTypeVar + } + // if arg of callee is a param of caller, propagate arg fro caller to callee + val args = callee.argumentsPassed.map { + case x if mode < AnalyseArgs => + val s = x.widenDealias.finalResultType.classSymbol.orElse(TypeErasure.erasure(x.widenDealias.finalResultType).classSymbol) + assert(s.exists) + ref(s).tpe + case x: PreciseType => + x + case x: ClosureType => + val utpe = normalizeType(propagateTargs(x.underlying, isConstructor = true)) + val outer = parentRefinements(utpe) ++ caller.outerTargs + val closureT = new ClosureType(x.meth, utpe, x.implementedMethod) + addReachableType(new TypeWithContext(closureT, outer)) + closureT + case x: TermRef if x.symbol.is(Param) && x.symbol.owner == caller.call.termSymbol => + val id = caller.call.termSymbol.info.paramNamess.iterator.flatten.indexWhere(_ == x.symbol.name) + caller.argumentsPassed(id) + case x => propagateTargs(x) + } + + def filterTypes(tp1: Type, tp2: Type, dtp1: Int, dtp2: Int): Boolean = { + if (dtp1 < dtp2) false + else if (mode >= AnalyseTypes) tp1 <:< tp2 + else { + val tp1w = tp1.widenDealias + val tp2w = tp2.widenDealias + val tp2c = tp2w.classSymbol.orElse(TypeErasure.erasure(tp2).classSymbol) + tp1w.derivesFrom(tp2c) + } + } + + def appliedToTargs(tp: Type): Type = + tp.widen.appliedTo(targs).widen + + def preciseSelectCall(tp: Type, sym: Symbol): TermRef = { + def selectCall(tp1: Type) = { + if (sym.is(Private) || tp.typeSymbol == sym.owner) sym + else { + val selectByName = tp1.nonPrivateMember(sym.name) + selectByName.suchThat(x => x == sym || x.overriddenSymbol(sym.owner.asClass) == sym).symbol + } + } + val call = selectCall(tp).orElse(if (tp.givenSelfType.exists) selectCall(tp.givenSelfType) else NoSymbol) + assert(call.exists) + TermRef(tp, call.asTerm) + } + + def dispatchCalls(receiverType: Type, dispatch: CallInfoWithContext => Unit = addCall): Unit = { + receiverType match { + case t: PreciseType => + dispatch(CallInfoWithContext(preciseSelectCall(t.underlying, calleeSymbol), targs, args, outerTargs, someCaller, someCallee)) + case t: ClosureType if calleeSymbol.name eq t.implementedMethod.name => + val methodSym = t.meth.meth.symbol.asTerm + dispatch(CallInfoWithContext(TermRef.withFixedSym(t.underlying, methodSym.name, methodSym), targs, t.meth.env.map(_.tpe) ++ args, outerTargs, someCaller, someCallee)) + case AndType(tp1, tp2) => + val set1 = mutable.Set.empty[CallInfoWithContext] + val set2 = mutable.Set.empty[CallInfoWithContext] + dispatchCalls(tp1, x => set1.add(x)) + dispatchCalls(tp2, x => set2.add(x)) + set1.intersect(set2).foreach(dispatch) + + case _ => + // without casts + val receiverTypeWiden = receiverType.widenDealias match { + case t: TypeRefWithFixedSym if t.classSymbol.owner.is(Method) => + new TypeRefWithFixedSym(t.classSymbol.owner.owner.typeRef, t.name, t.fixedSym) + case t => t + } + + def denotMatch(tp: TypeWithContext, p: Symbol) = { + val d1 = p.asSeenFrom(tp.tp) + val d2 = calleeSymbol.asSeenFrom(tp.tp) + (d1 eq d2) || d1.matches(d2) + } + + for ((dtp, tp) <- getTypesByMemberName(calleeSymbol.name)) { + if (filterTypes(tp.tp, receiverTypeWiden, dtp, receiverDepth)) { + for (alt <- tp.tp.member(calleeSymbol.name).altsWith(p => denotMatch(tp, p))) { + if (alt.exists) + dispatch(CallInfoWithContext(TermRef(tp.tp, alt.symbol.asTerm), targs, args, outerTargs ++ tp.outerTargs, someCaller, someCallee)) + } + } + } + + if (mode >= AnalyseTypes) { + lazy val filterCastCachedFilter = { + val receiverBases = receiverType.widen.classSymbols.iterator + val head = receiverBases.next() + receiverBases.foldLeft(castCache1.getOrDefault(head, Set.empty)){case (a,b) => a.intersect(castCache1.getOrDefault(b, Set.empty))} + } + for ((tpDepth, tp) <- getTypesByMemberName(calleeSymbol.name)) { + for (cast <- castsCache.getOrElse(tp, Set.empty[Cast])) { + if (filterCastCachedFilter.contains(cast)) { + if (filterTypes(tp.tp, cast.from, tpDepth, cast.fromDepth) && filterTypes(cast.to, receiverType, cast.toDepth, receiverDepth)) { + for (alt <- tp.tp.member(calleeSymbol.name).altsWith(p => p.matches(calleeSymbol.asSeenFrom(tp.tp)))) { + if (alt.exists) { + if (!addingOutEdges) { + // this additionally introduces a cast of result type and argument types + val uncastedSig = preciseSelectCall(tp.tp, alt.symbol).widen.appliedTo(targs).widen + val castedSig = preciseSelectCall(receiverType, calleeSymbol).widen.appliedTo(targs).widen + (uncastedSig.paramInfoss.iterator.flatten zip castedSig.paramInfoss.iterator.flatten) foreach (x => addCast(x._2, x._1)) + addCast(uncastedSig.finalResultType, castedSig.finalResultType) // FIXME: this is added even in and tpe that are out of the intersection + } + + dispatch(CallInfoWithContext(tp.tp.select(alt.symbol).asInstanceOf[TermRef], targs, args, outerTargs ++ tp.outerTargs, someCaller, someCallee)) + } + } + } + } + } + } + } + } + } + + receiver match { + case _ if calleeSymbol == defn.Predef_classOf => + classOfs.put(callee.targs.head.classSymbol, ()) + case _ if calleeSymbol == ctx.definitions.throwMethod => + case _ if calleeSymbol == ctx.definitions.Any_asInstanceOf => + val from = propagateTargs(receiver) + val to = propagateTargs(targs.head) + addCast(from, to) + case _ if defn.ObjectMethods.contains(calleeSymbol) || defn.AnyMethods.contains(calleeSymbol) => Nil + case NoPrefix => // inner method + assert(calleeSymbol.is(ParamAccessor) || calleeSymbol.owner.is(Method) || calleeSymbol.owner.isLocalDummy) + addCall(CallInfoWithContext(TermRef.withFixedSym(caller.call.normalizedPrefix, calleeSymbol.name, calleeSymbol), targs, args, outerTargs, someCaller, someCallee)) + + case t if calleeSymbol.isConstructor => + + val constructedType = appliedToTargs(callee.call).finalResultType + val fixNoPrefix = if (constructedType.normalizedPrefix eq NoPrefix) { + @tailrec def getPrefix(currentPrefix: Type): Type = { + if (currentPrefix.classSymbol.exists) currentPrefix + else if (currentPrefix.termSymbol.is(Module)) getPrefix(currentPrefix.widenDealias) + else { + getPrefix(currentPrefix.normalizedPrefix match { + case t: ThisType => t.tref + case t => t + }) + } + } + constructedType match { + case constructedType @ TypeRef(prefix, name) => + constructedType.underlying match { + case ci: ClassInfo => + val currentPrefix = getPrefix(caller.call.normalizedPrefix) + val nci = ci.derivedClassInfo(prefix = currentPrefix) // todo: do this only for inner anonym classes + TypeRef.withFixedSymAndInfo(currentPrefix, name, constructedType.symbol.asType, nci) + } + } + } else constructedType + + val tpe = propagateTargs(fixNoPrefix, isConstructor = true) + addReachableType(new TypeWithContext(tpe, parentRefinements(tpe) ++ outerTargs)) + + val call = { + if (callerSymbol.isConstructor && callerSymbol.owner == calleeSymbol.owner) + new TermRefWithFixedSym(calleeSymbol.owner.typeRef, calleeSymbol.name, calleeSymbol) + else + propagateTargs(receiver).select(calleeSymbol).asInstanceOf[TermRef] + } + + addCall(CallInfoWithContext(call, targs, args, outerTargs, someCaller, someCallee)) + + // super call in a class (know target precisely) + case st: SuperType => + val thisTpe = st.thistpe + val targetClass = st.supertpe.baseClasses.find(clz => + clz.info.decl(calleeSymbol.name).altsWith(p => p.signature == calleeSymbol.signature).nonEmpty + ) + val targetMethod = targetClass.get.info.member(calleeSymbol.name).altsWith(p => p.signature == calleeSymbol.signature).head.symbol.asTerm + val thisTpePropagated = propagateTargs(thisTpe) + + + addCall(CallInfoWithContext(TermRef.withFixedSym(thisTpePropagated, targetMethod.name, targetMethod), targs, args, outerTargs, someCaller, someCallee)) + + // super call in a trait + case t if calleeSymbol.is(Scala2SuperAccessor) => + + // Taken from ResolveSuper.rebindSuper + val SuperAccessorName(memberName) = calleeSymbol.name.unexpandedName + + val prev = t.widenDealias.classSymbol + getTypesByMemberName(memberName).foreach { + x => + val s = x._2.tp.baseClasses.dropWhile(_ != prev) + if (s.nonEmpty) { + val parentMethod = ResolveSuper.rebindSuper(x._2.tp.widenDealias.classSymbol, calleeSymbol).asTerm + // todo: outerTargs are here defined in terms of location of the subclass. Is this correct? + addCall(CallInfoWithContext(TermRef.withFixedSym(t, parentMethod.name, parentMethod), targs, args, outerTargs, someCaller, someCallee)) + + } + } + + case thisType: ThisType if !calleeSymbol.owner.flags.is(PackageCreationFlags) => + val dropUntil = thisType.tref.classSymbol + @tailrec def getPreciseThis(currentThis: Type, currentOwner: Symbol): Type = { + if ((currentOwner eq dropUntil) || currentOwner.owner.is(Package) || (currentThis eq NoType)) currentThis + else if (currentOwner.is(Method)) getPreciseThis(currentThis, currentOwner.owner.enclosingClass) + else getPreciseThis(currentThis.normalizedPrefix, currentOwner.owner.enclosingClass) + } + + val currentThis = getPreciseThis(caller.call.normalizedPrefix, caller.call.termSymbol.owner) + + if (!currentThis.derivesFrom(thisType.cls)) + dispatchCalls(propagateTargs(receiver.widenDealias)) + else if (calleeSymbol.is(Private)) + addCall(CallInfoWithContext(TermRef.withFixedSym(currentThis, calleeSymbol.name, calleeSymbol), targs, args, outerTargs, someCaller, someCallee)) + else + dispatchCalls(propagateTargs(AndType.apply(currentThis, thisType.tref))) + + + case _: PreciseType => + dispatchCalls(propagateTargs(receiver)) + case _: ClosureType => + dispatchCalls(propagateTargs(receiver)) + case x: TermRef if x.symbol.is(Param) && x.symbol.owner == caller.call.termSymbol => + val a = propagateTargs(receiver) + dispatchCalls(a) + case _ => + dispatchCalls(propagateTargs(receiver.widenDealias)) + } + } + + private def processCallSites(): Unit = { + var processed = 0 + var total = 0 + + def computeCRC(tp: Type) = tp.classSymbols.iterator.map(x => sizesCache.getOrDefault(x, 0)).sum + + for (method <- reachableMethods) { + lazy val crcMap = lastInstantiation.getOrElseUpdate(method, mutable.Map.empty) + // Find new call sites + + def needsCallSiteInstantiation(callSite: CallInfo): Boolean = { + total += 1 + val receiver = callSite.call.normalizedPrefix + val recomputedCRC = computeCRC(receiver) + val needsInstantiation = recomputedCRC != crcMap.getOrElse(callSite, -1) + if (needsInstantiation) + crcMap(callSite) = recomputedCRC + needsInstantiation || addingOutEdges || finished // if finished==true we are checking the completeness of the CRC + } + + collectedSummaries.get(method.callSymbol).orElse(tastySummaries(method.callSymbol)) match { + case Some(summary) => + summary.definedClosures.foreach(x => addReachableClosure(x, method)) + + for (methodCalled <- summary.methodsCalled) { + for (callSite <- methodCalled._2) { + if (needsCallSiteInstantiation(callSite)) { + processed += 1 + instantiateCallSite(method, callSite, _ => true) + } + } + } + + case None => + + if (!outerMethods.containsKey(method.callSymbol)) { + + outerMethods.put(method.callSymbol, ()) + + if (withJavaCallGraph && !method.callSymbol.is(Module | Package) && !method.parent.exists(_.isOnJavaAllocatedType)) { + + addReachableJavaReturnType(method) + + // Add all possible calls from java to object passed as parameters. + for (rec <- (method.call.normalizedPrefix :: method.argumentsPassed).distinct) { + for (potentialCall <- allPotentialCallsFor(rec)) { + if (needsCallSiteInstantiation(potentialCall) && method.getOutEdges(potentialCall).isEmpty) { + processed += 1 + instantiateCallSite(method, potentialCall, call => collectedSummaries.contains(call.callSymbol)) + } + } + } + } + } + } + } + + ctx.log(s"Processed $processed calls out of $total") + } + + private def addReachableJavaReturnType(method: CallInfoWithContext): Unit = { + val tParamNames = method.call.widenDealias.typeParams.map(_.paramName) + val newOuterTargs = method.outerTargs.addAll(method.callSymbol, tParamNames, method.targs) + + def substituteOuterTargs = new SubstituteByParentMap(newOuterTargs) + + // Add return type to reachable types + val methodTpe = method.call.widenDealias + + val returnType = methodTpe match { + case t: PolyType => t.instantiate(method.targs).finalResultType + case _ => methodTpe.finalResultType + } + + val javaAllocatedType = returnType match { + case returnType: JavaAllocatedType => returnType + case returnType: HKApply => new JavaAllocatedType(substituteOuterTargs(returnType.tycon.appliedTo(method.targs))) + case returnType => new JavaAllocatedType(substituteOuterTargs(returnType)) + } + addReachableType(new TypeWithContext(javaAllocatedType, OuterTargs.empty)) + } + + private def allPotentialCallsFor(argType: Type): Set[CallInfo] = { + if (defn.isPrimitiveClass(argType.classSymbol)) { + Set.empty + } else { + def potentialCall(decl: Symbol): Option[CallInfo] = { + lazy val paramTypes = decl.info.paramInfoss.flatten + val call = new TermRefWithFixedSym(argType, decl.name.asTermName, decl.asTerm) + val targs = call.widenDealias match { + case call: PolyType => + def erasedBounds(tp: TypeBounds): Type = tp.hi match { + case RefinedType(parent, refinedName, refinedInfo: TypeBounds) => + RefinedType(parent, refinedName, TypeAlias(erasedBounds(refinedInfo))) + case t => t + } + call.paramInfos.map(erasedBounds) + + case _ => Nil + } + + def isDefinedInJavaClass(sym: Symbol) = + sym.owner == defn.AnyClass || sym.owner.is(JavaDefined) + + val definedInJavaClass: Boolean = + isDefinedInJavaClass(decl) || decl.allOverriddenSymbols.exists(isDefinedInJavaClass) + + argType match { + case argType: PreciseType => + if (!definedInJavaClass) None + else Some(CallInfo(call, targs, paramTypes)) + + case _ => + val argTypeWiden = argType.widenDealias + lazy val sym = argTypeWiden.classSymbol.requiredMethod(decl.name, paramTypes) + if (paramTypes.exists(_.typeSymbol.isTypeParam)) { + // println(s"Ignoring `${decl.name}` in java call graph construction because type parameters are not suported yet") + None + } else if (!argTypeWiden.member(decl.name).exists || !definedInJavaClass || (isDefinedInJavaClass(decl) && sym.isEffectivelyFinal)) None + else Some(CallInfo(TermRef(argType, sym), targs, paramTypes)) + + } + } + + def allDecls(argType: Type) = + argType.decls.iterator.toSet ++ argType.parents.flatMap(_.decls.iterator) + + for { + decl <- allDecls(argType) + if decl.isTerm && !decl.isConstructor && decl.name != nme.COMPANION_MODULE_METHOD + if decl.name != nme.isInstanceOf_ && decl.name != nme.asInstanceOf_ && decl.name != nme.synchronized_ + callInfo <- potentialCall(decl) + } yield callInfo + } + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallInfoWithContext.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallInfoWithContext.scala new file mode 100644 index 000000000000..cd9a57a984d9 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallInfoWithContext.scala @@ -0,0 +1,62 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.linker.summaries.{AbstractCallInfo, CallInfo} +import dotty.tools.dotc.transform.linker.types._ + +import scala.collection.mutable + +class CallInfoWithContext private (val call: TermRef, val targs: List[Type], val argumentsPassed: List[Type], + val outerTargs: OuterTargs, val parent: Option[CallInfoWithContext], val callee: Option[CallInfo]) + extends AbstractCallInfo { + + private lazy val outEdges = mutable.HashMap[CallInfo, List[CallInfoWithContext]]().withDefault(x => Nil) + + def outEdgesIterator: Iterator[(CallInfo, List[CallInfoWithContext])] = outEdges.iterator + + def getOutEdges(callSite: CallInfo): List[CallInfoWithContext] = outEdges(callSite) + + def addOutEdges(callSite: CallInfo, edges: Traversable[CallInfoWithContext]): Unit = { + var es = outEdges(callSite) + for (e <- edges) { + if (!es.contains(e)) + es = e :: es + } + outEdges(callSite) = es + } + + def addOutEdges(callSite: CallInfo, e: CallInfoWithContext): Unit = { + val es = outEdges(callSite) + if (!es.contains(e)) + outEdges(callSite) = e :: es + } + + def edgeCount: Int = + outEdges.values.foldLeft(0)(_ + _.size) + + def source: Option[CallInfo] = callee.flatMap(_.source) + + def isOnJavaAllocatedType: Boolean = call.prefix.isInstanceOf[JavaAllocatedType] + + override def equals(obj: Any): Boolean = obj match { + case obj: CallInfoWithContext => + call == obj.call && targs == obj.targs && argumentsPassed == obj.argumentsPassed && outerTargs == obj.outerTargs + case _ => false + } + + override def hashCode(): Int = call.hashCode ^ targs.hashCode ^ argumentsPassed.hashCode ^ outerTargs.mp.hashCode + + override def toString(): String = s"CallInfoWithContext($call, $targs, $argumentsPassed, $outerTargs, $parent, $callee)" +} + +object CallInfoWithContext { + + def apply(call: TermRef, targs: List[Type], argumentsPassed: List[Type], outerTargs: OuterTargs, + parent: Option[CallInfoWithContext], callee: Option[CallInfo])(implicit ctx: Context): CallInfoWithContext = { + val callInfo = new CallInfoWithContext(call, targs, argumentsPassed, outerTargs, parent, callee) + AbstractCallInfo.assertions(callInfo) + callInfo + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/Cast.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/Cast.scala new file mode 100644 index 000000000000..eb8db3d9b949 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/Cast.scala @@ -0,0 +1,27 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Types.{MethodicType, Type} + +class Cast(val from: Type, val to: Type)(implicit ctx: Context) { + + lazy val fromDepth = TypeDepth(from) + lazy val toDepth = TypeDepth(to) + + assertNonMethodic("from", from.widenDealias) + assertNonMethodic("to", to.widenDealias) + + override def equals(other: Any): Boolean = other match { + case other: Cast => fromDepth == other.fromDepth && toDepth == other.toDepth && other.from =:= from && other.to =:= to + case _ => false + } + + override def hashCode(): Int = + from.typeSymbol.hashCode() * 31 + to.typeSymbol.hashCode() + + override def toString: String = s"Cast($from, $to)" + + private def assertNonMethodic(name: String, tpe: Type): Unit = + assert(!tpe.isInstanceOf[MethodicType], name + " = " + tpe) + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/GraphVisualization.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/GraphVisualization.scala new file mode 100644 index 000000000000..742f3cdb1f8e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/GraphVisualization.scala @@ -0,0 +1,482 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Names._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.linker.summaries.{AbstractCallInfo, CallInfo} + +import scala.collection.mutable + +object GraphVisualization { + + @deprecated("replaced with outputGraphVis", "0") + def outputGraph(mode: Int, specLimit: Int)(callGraph: CallGraph)(implicit ctx: Context): String = { + val reachableMethods = callGraph.reachableMethods + val reachableTypes = callGraph.reachableTypes + val outerMethod = callGraph.outerMethods + + val outGraph = new StringBuffer() + outGraph.append(s"digraph Gr${mode}_$specLimit {\n") + outGraph.append("graph [fontsize=10 fontname=\"Verdana\" compound=true];\n") + outGraph.append("label = \"" + reachableMethods.size + " nodes, " + + reachableMethods.foldLeft(0)(_ + _.edgeCount) + " edges, " + reachableTypes.size + " reachable types\";\n") + + // add names and subraphs + reachableMethods.foreach { caller => + val subgraphColor = if (outerMethod.contains(caller.callSymbol)) "red" else "blue" + + outGraph.append("subgraph ").append(clusterName(caller)).append(" {\n") + outGraph.append(" label = ").append(slash + csWTToName(caller) + slash).append(";\n") + outGraph.append(" color = ").append(subgraphColor).append(";\n") + outGraph.append(" ").append(dummyName(caller)).append(" [shape=point style=invis];\n") + for ((call, _) <- caller.outEdgesIterator) { + outGraph.append(" ").append(csToName(caller, call)) + outGraph.append(" [") + outGraph.append("label=").append(slash).append(callSiteLabel(call)).append(slash) + call.source match { + case Some(source) => + outGraph.append(", color=") + val color = + if (source.call.widenDealias.classSymbol.is(JavaDefined)) "orange" + else "blue" + outGraph.append(color) + case None => + } + outGraph.append("];\n") + } + outGraph.append("}\n\n") + } + + // Add edges + reachableMethods.foreach { caller => + for ((callInfo, edges) <- caller.outEdgesIterator) + edges.foreach { target => + val from = csToName(caller, callInfo) + val to = dummyName(target) + outGraph.append(from).append(" -> ").append(to) + outGraph.append(" [") + outGraph.append("ltail=").append(clusterName(target)) + outGraph.append("];\n") + } + outGraph.append("\n") + } + outGraph.append("}") + outGraph.toString + } + + def outputGraphVisToFile(callGraph: CallGraph, outFile: java.io.File)(implicit ctx: Context): Unit = { + val p = new java.io.PrintWriter(outFile) + try { + outputGraphVis(callGraph, p) + } finally { + p.close() + } + } + + private def outputGraphVis(callGraph: CallGraph, pw: java.io.PrintWriter)(implicit ctx: Context): Unit = { + val reachableMethods = callGraph.reachableMethods + val reachableTypes = callGraph.reachableTypes + val outerMethod = callGraph.outerMethods + + val callInfoNodeId = mutable.Map.empty[CallInfoWithContext, String] + + // add names and subraphs + val nodes = mutable.Map.empty[String, String] + val edges = mutable.Map.empty[String, List[String]] + + def addNode(id: String, properties: String*): Unit = { + nodes(id) = (Iterator(s"id: '$id'") ++ properties).mkString("{ ", ", ", " }") + } + + def addEdge(from: String, to: String, properties: String*): Unit = { + val newEntry = (Iterator(s"from: '$from'", s"to: '$to'") ++ properties).mkString("{ ", ", ", " }") + edges(from) = newEntry :: edges.getOrElse(from, Nil) + } + + val red = "'rgb(255,150,150)'" + val lightRed = "'rgb(255,200,200)'" + val blue = "'rgb(150,150,255)'" + val lightBlue = "'rgb(200,200,255)'" + val green = "'rgb(150,255,150)'" + val grey = "'rgb(200,200,200)'" + + def detailsHTML(info: CallInfoWithContext): String = { + htmlFormattedStringLabel( + s"""call: ${info.call.show} + |targs: ${info.targs.map(_.show)} + |arguments: ${info.argumentsPassed.map(_.show)} + | + |id: ${info.id} + """.stripMargin) + } + + callGraph.entryPoints.values.toSet[Int].foreach { entryPointId => + addNode("entry-" + entryPointId, "shape: 'diamond'", "color: " + green) + } + + reachableMethods.map(_.call).foreach { call => + val widenCall = call.widenDealias + val callId = "call-" + widenCall.uniqId + + val label = htmlFormattedStringLabel(call.termSymbol.owner.name + "." + call.termSymbol.name + (widenCall match { + case widenCall: PolyType => widenCall.paramRefs.map(_.show).mkString("[", ",", "]") + widenCall.resType.show + case widenCall => widenCall.show + })) + addNode(callId, s"label: '$label'", s"color: $grey") + } + + reachableMethods.foreach { caller => + + val color = + if (outerMethod.contains(caller.callSymbol)) red + else blue + + val callId = "call-" + caller.call.widenDealias.uniqId + val callerId = callInfoNodeId.getOrElseUpdate(caller, caller.id.toString) + + addNode(callerId, s"label: '${csWTToName(caller)}'", s"title: '${detailsHTML(caller)}'", s"color: $color") + addEdge(callerId, callId, s"title: 'actually calls'", s"color: $grey") + + callGraph.entryPoints.get(caller) match { + case Some(entryPointId) => + addEdge("entry-" + entryPointId, callerId) + case None => + } + + for ((call, callees) <- caller.outEdgesIterator) { + val callId = callerId + "-" + call.id + val color = + if (outerMethod.contains(caller.callSymbol)) lightRed + else lightBlue + addNode(callId, s"label: '${callSiteLabel(call)}'", s"color: $color") + + if (call.source.isEmpty) + addEdge(callerId, callId, s"title: 'calls'") + + callees.foreach { callee => + val calleeId = callInfoNodeId.getOrElseUpdate(callee, callee.id.toString) + addEdge(callId, calleeId, s"title: 'dispatches to'") + + callee.source.foreach { source => + val actualCallerId = callerId + "-" + source.id + val calleeId = callee.id.toString + addEdge(actualCallerId, calleeId, s"title: 'also dispatches call to'") + } + } + } + } + + def printNodesJSON(): Unit = { + def printNodes(nodes: List[(String, String)]): Unit = nodes match { + case n :: ns => + pw.print("'" + n._1 + "': " + n._2) + if (ns.nonEmpty) + pw.print(",") + printNodes(ns) + case Nil => + } + pw.println("{") + printNodes(nodes.toList) + pw.println("}") + } + def printEdgesJSON(): Unit = { + def printEdges(edges: List[(String, List[String])]): Unit = edges match { + case e :: es => + pw.print("'" + e._1 + "': ") + pw.print("[") + pw.print(e._2.mkString(",")) + pw.print("]") + if (es.nonEmpty) + pw.print(",") + printEdges(es) + case Nil => + } + pw.println("{") + printEdges(edges.toList) + pw.println("}") + } + + pw.println(""" + | + | + | + | Callgraph + | + | + | + | + | + | + | + |
+ |
+ |
+ |
+ |
0%
+ |
+ |
+ |
+ |
+ |
+ |
+ | + | + | + | + | + | + """.stripMargin) + } + + private def callSiteLabel(x: CallInfo)(implicit ctx: Context): String = { + val prefix = x.call.normalizedPrefix + val calleeSymbol = x.callSymbol + val prefixString = prefix match { + case NoPrefix => calleeSymbol.name.toString + case t if calleeSymbol.isPrimaryConstructor => calleeSymbol.showFullName + case st: SuperType => s"super[${st.supertpe.classSymbol.showFullName}].${calleeSymbol.name}" + /* case t if calleeSymbol.is(SuperAccessor) => + val prev = t.classSymbol + types.flatMap { + x => + val s = x.baseClasses.dropWhile(_ != prev) + if (s.nonEmpty) { + val parent = s.find(x => x.info.decl(calleeSymbol.name).altsWith(x => x.signature == calleeSymbol.signature).nonEmpty) + parent match { + case Some(p) if p.exists => + val method = p.info.decl(calleeSymbol.name).altsWith(x => x.signature == calleeSymbol.signature) + // todo: outerTargs are here defined in terms of location of the subclass. Is this correct? + new CallWithContext(t.select(method.head.symbol), targs, args, outerTargs) :: Nil + case _ => Nil + } + } else Nil + } */ + + case thisType: ThisType => + "this." + calleeSymbol.name + case t => + typeName(t) + '.' + calleeSymbol.name + } + + val targsString = typeArgumentsString(x.targs) + val vargsString = argumentsString(x.argumentsPassed) + + htmlFormattedStringLabel(prefixString + targsString + vargsString) + } + + private val slash = '"' + + private def htmlFormattedStringLabel(str: String): String = + str.replace("\n", "
").replace("'", "\"") + + private def escape(s: String) = s.replace("\\", "\\\\") + + private def fullNameSeparated(symbol: Symbol)(separator: String)(implicit ctx: Context): Name = { + var sep = separator + val owner = symbol.owner + var name: Name = symbol.name + var stopAtPackage = false + if (sep.isEmpty) { + sep = "$" + stopAtPackage = true + } + if (symbol.isAnonymousClass || symbol.isAnonymousFunction) + name = name ++ symbol.id.toString + if (symbol == NoSymbol || + owner == NoSymbol || + owner.isEffectiveRoot || + stopAtPackage && owner.is(PackageClass)) name + else { + var encl = owner + while (!encl.isClass && !encl.isPackageObject) { + encl = encl.owner + sep += "~" + } + if (owner.is(ModuleClass, butNot = Package) && sep == "$") sep = "" // duplicate scalac's behavior: don't write a double '$$' for module class members. + val fn = fullNameSeparated(encl)(separator) ++ sep ++ name + if (symbol.isType) fn.toTypeName else fn.toTermName + } + } + + private def symbolName(sym: Symbol)(implicit ctx: Context): String = { + if (!sym.is(Method)) escape(sym.name.show) + else escape(fullNameSeparated(sym)(".").show) + } + + private def typeName(x: Type)(implicit ctx: Context): String = { + x match { + case ConstantType(value) => escape(value.toString) + case _ => + val t = x.termSymbol.orElse(x.typeSymbol) + if (t.exists) + symbolName(t) + else escape(x.show) + } + } + + private def csWTToName(x: AbstractCallInfo)(implicit ctx: Context): String = { + val targs = typeArgumentsString(x.targs) + val vargs = typeParameterString(x.call.widenDealias.paramInfoss) + val resultType = typeName(x.call.widenDealias.finalResultType) + if (x.callSymbol.owner == x.call.normalizedPrefix.classSymbol) { + val callTypeName = typeName(x.call) + htmlFormattedStringLabel(callTypeName + targs + vargs + ": " + resultType) + } else { + val callTypeName = typeName(x.call.normalizedPrefix) + val symName = symbolName(x.callSymbol) + htmlFormattedStringLabel(callTypeName + ".super." + symName + targs + vargs + ": " + resultType) + } + } + + private def csWTToShortName(x: AbstractCallInfo)(implicit ctx: Context): String = { + if (x.callSymbol.owner.name == x.call.normalizedPrefix.classSymbol.name) { + val callTypeName = typeName(x.call) + callTypeName + } else { + val callTypeName = typeName(x.call.normalizedPrefix) + symbolName(x.callSymbol) + } + } + + private def csToName(parent: CallInfoWithContext, inner: CallInfo)(implicit ctx: Context): String = { + slash + csWTToName(parent) + escape(inner.call.show) + inner.hashCode() + slash + } + + private def dummyName(x: AbstractCallInfo)(implicit ctx: Context): String = { + slash + csWTToName(x) + "_Dummy" + slash + } + + private def clusterName(x: AbstractCallInfo)(implicit ctx: Context): String = { + slash + "cluster_" + csWTToName(x) + slash + } + + private def argumentsString(args: List[Type])(implicit ctx: Context): String = { + if (args.isEmpty) "" + else args.map(typeName).mkString("(", ",", ")") + } + + private def typeArgumentsString(targs: List[Type])(implicit ctx: Context): String = { + if (targs.isEmpty) "" + else targs.map(t => typeName(t.widenDealias)).mkString("[", ",", "]") + } + + private def typeParameterString(paramTypess: List[List[Type]])(implicit ctx: Context): String = { + paramTypess.iterator.map { paramTypes => + paramTypes.map(x => typeName(x)).mkString("(", ",", ")") + }.mkString("") + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/OuterTargs.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/OuterTargs.scala new file mode 100644 index 000000000000..d48c39923c79 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/OuterTargs.scala @@ -0,0 +1,60 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Types.{RefinedType, Type, TypeAccumulator, TypeAlias, TypeType} +import dotty.tools.dotc.transform.linker.types.ClosureType + +object OuterTargs { + val empty = new OuterTargs(Map.empty) + + def parentRefinements(tp: Type)(implicit ctx: Context): OuterTargs = { + new TypeAccumulator[OuterTargs]() { + def apply(x: OuterTargs, tp: Type): OuterTargs = tp match { + case t: RefinedType => + val member = t.parent.member(t.refinedName).symbol + val parent = member.owner + val nList = x.add(parent, t.refinedName, t.refinedInfo) + apply(nList, t.parent) + case t: ClosureType => + apply(x, t.u) + case _ => + foldOver(x, tp) + } + }.apply(OuterTargs.empty, tp) + } +} + +final class OuterTargs(val mp: Map[Symbol, Map[Name, Type]]) extends AnyVal { + + def nonEmpty: Boolean = mp.nonEmpty + + def add(parent: Symbol, tp: Type)(implicit ctx: Context): OuterTargs = { + assert(!parent.isClass || tp.isInstanceOf[TypeType], tp) + this.add(parent, tp.typeSymbol.name, tp) + } + + def add(parent: Symbol, name: Name, tp: Type)(implicit ctx: Context): OuterTargs = { + val tp1 = if (parent.isClass && !tp.isInstanceOf[TypeType]) TypeAlias(tp) else tp + val old = mp.getOrElse(parent, Map.empty) + new OuterTargs(mp.updated(parent, old + (name -> tp1))) + } + + def addAll(parent: Symbol, names: List[Name], tps: List[Type])(implicit ctx: Context): OuterTargs = + (names zip tps).foldLeft(this)((x, nameType) => x.add(parent, nameType._1, nameType._2)) + + def ++(other: OuterTargs)(implicit ctx: Context): OuterTargs = { + other.mp.foldLeft(this) { (x, y) => + y._2.foldLeft(x: OuterTargs)((x: OuterTargs, z: (Name, Type)) => x.add(y._1, z._1, z._2)) + } + } + + def combine(environment: OuterTargs)(implicit ctx: Context): OuterTargs = { + val subst = new SubstituteByParentMap(environment) + val newMap = mp.map(x => (x._1, x._2.map(x => (x._1, subst.apply(x._2))))) + new OuterTargs(newMap) + } + + override def toString: String = s"OuterTargs($mp)" +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/SubstituteByParentMap.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/SubstituteByParentMap.scala new file mode 100644 index 000000000000..ce4cb6f30f3c --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/SubstituteByParentMap.scala @@ -0,0 +1,58 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.linker.types.ClosureType + +class SubstituteByParentMap(substMap: OuterTargs)(implicit ctx1: Context) extends DeepTypeMap()(ctx1) { + + def apply(tp: Type): Type = { + lazy val substitution = substMap.mp.getOrElse(tp.typeSymbol.owner, Nil) + def termTypeIfNeed(t: Type): Type = { + if (tp.isInstanceOf[TermType] && !t.isInstanceOf[TermType]) { + t match { + case t: TypeAlias => + assert(t.underlying.isInstanceOf[TermType]) + t.underlying + case t: ClassInfo => + t.typeRef + case _ => + assert(false) + null + } + } else t + } + val res = tp match { + case tp: RefinedType => mapOver(tp) // otherwise we will loose refinement + case tp: TypeAlias => mapOver(tp) // map underlying + case tp: HKApply => mapOver(tp) + case _ if tp.typeSymbol.exists && substitution.nonEmpty => + var typ = tp + var id = substitution.find(x => x._1 == tp.typeSymbol.name) + var limit = 30 + var stack: List[Type] = Nil + while (id.isEmpty && (limit > 0) && (typ.typeSymbol.info.typeSymbol ne typ.typeSymbol)) { + typ = typ.typeSymbol.info + stack = typ :: stack + id = substitution.find(x => x._1 == typ.typeSymbol.name) + limit -= 1 + } + + if (id.isDefined) { + val t = termTypeIfNeed(id.get._2.stripTypeVar) + if (!(t =:= typ)) + apply(termTypeIfNeed(t)) + else t + } else tp + case t: TypeRef if t.prefix.normalizedPrefix eq NoPrefix => + val tmp = apply(t.info) + if (tmp ne t.info) termTypeIfNeed(tmp) + else mapOver(t) + case tp: ClosureType => + new ClosureType(tp.meth, apply(tp.underlying), tp.implementedMethod) + case _ => mapOver(tp) + } + assert(!(tp.isInstanceOf[TypeType] ^ res.isInstanceOf[TypeType]), (tp, res)) + res + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/TypeDepth.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/TypeDepth.scala new file mode 100644 index 000000000000..08bae6655705 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/TypeDepth.scala @@ -0,0 +1,16 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ + +object TypeDepth { + + def apply(tp: Type)(implicit ctx: Context): Int = + max(tp.classSymbols.iterator.map(classDepth)) + + private def classDepth(cls: ClassSymbol)(implicit ctx: Context): Int = + 1 + max(cls.classParents.iterator.map(apply)) + + private def max(it: Iterator[Int]) = if (it.hasNext) it.max else 0 +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/TypeWithContext.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/TypeWithContext.scala new file mode 100644 index 000000000000..debc3d615607 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/TypeWithContext.scala @@ -0,0 +1,15 @@ +package dotty.tools.dotc.transform.linker.callgraph + +import dotty.tools.dotc.core.Types.Type + +class TypeWithContext(val tp: Type, val outerTargs: OuterTargs) { + + override def equals(obj: Any): Boolean = obj match { + case obj: TypeWithContext => tp == obj.tp && outerTargs == obj.outerTargs + case _ => false + } + + override def hashCode(): Int = tp.hashCode() // ^ outerTargs.mp.hashCode() + + override def toString: String = s"TypeWithContext($tp, $outerTargs)" +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/summaries/AbstractCallInfo.scala b/compiler/src/dotty/tools/dotc/transform/linker/summaries/AbstractCallInfo.scala new file mode 100644 index 000000000000..43a8a65e45fa --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/summaries/AbstractCallInfo.scala @@ -0,0 +1,47 @@ +package dotty.tools.dotc.transform.linker.summaries + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols.TermSymbol +import dotty.tools.dotc.core.Types.{MethodType, PolyType, TermRef, Type, TypeBounds} +import dotty.tools.dotc.transform.linker.types.ClosureType +import dotty.tools.sharable + +abstract class AbstractCallInfo { + + final val id: Int = AbstractCallInfo.nextId() + + /** This is type of method, that includes full type of receiver, eg: TermRef(receiver, Method) */ + val call: TermRef + + /** Type arguments at call site */ + val targs: List[Type] + + /** Type of the arguments at call site */ + val argumentsPassed: List[Type] + + def callSymbol(implicit ctx: Context): TermSymbol = call.normalizedPrefix match { + case t: ClosureType => t.meth.meth.symbol.asTerm + case _ => call.termSymbol.asTerm + } +} + +object AbstractCallInfo { + + def assertions(callInfo: AbstractCallInfo)(implicit ctx: Context): Unit = { + callInfo.call.widenDealias match { + case t: PolyType => assert(t.paramNames.size == callInfo.targs.size) + case t: MethodType => assert(t.paramNamess.flatten.size == callInfo.argumentsPassed.size) + case _ => + } + callInfo.argumentsPassed.foreach(arg => assert(arg.widen.isValueType, arg)) + callInfo.targs.foreach(targ => assert(!targ.isInstanceOf[TypeBounds], targ)) + } + + @sharable private var lastId = 0 + + private[AbstractCallInfo] def nextId(): Int = { + lastId += 1 + lastId + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/summaries/CallInfo.scala b/compiler/src/dotty/tools/dotc/transform/linker/summaries/CallInfo.scala new file mode 100644 index 000000000000..1f9a998a168d --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/summaries/CallInfo.scala @@ -0,0 +1,33 @@ +package dotty.tools.dotc.transform.linker.summaries + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.linker.types.TypeNormalizer + +/* When source is not None this call was generated as part of a call to source */ +class CallInfo private (val call: TermRef, val targs: List[Type], val argumentsPassed: List[Type], + val source: Option[CallInfo]) extends AbstractCallInfo { + + override def equals(obj: Any): Boolean = obj match { + case obj: CallInfo => + call == obj.call && targs == obj.targs && argumentsPassed == obj.argumentsPassed + case _ => false + } + + override def hashCode(): Int = call.hashCode ^ targs.hashCode ^ argumentsPassed.hashCode + +} + +object CallInfo { + + def apply(call: TermRef, targs: List[Type], argumentsPassed: List[Type], source: Option[CallInfo] = None)(implicit ctx: Context): CallInfo = { + val normalizeType = new TypeNormalizer() + val normalCall = normalizeType(call).asInstanceOf[TermRef] + val normalTargs = targs.map(x => normalizeType(x)) + val normalArgumentsPassed = argumentsPassed.map(x => normalizeType(x)) + val callInfo = new CallInfo(normalCall, normalTargs, normalArgumentsPassed, source) + AbstractCallInfo.assertions(callInfo) + callInfo + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/summaries/MethodSummary.scala b/compiler/src/dotty/tools/dotc/transform/linker/summaries/MethodSummary.scala new file mode 100644 index 000000000000..2ec43a2bddc9 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/summaries/MethodSummary.scala @@ -0,0 +1,14 @@ +package dotty.tools.dotc.transform.linker.summaries + +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.transform.linker.types.ClosureType + +class MethodSummary(val methodDef: Symbol, + val thisAccessed: Boolean, + val methodsCalled: Map[Type, List[CallInfo]], + val definedClosures: List[ClosureType], + val accessedModules: List[Symbol], + val argumentReturned: Byte, // -1 if not known + val argumentStoredToHeap: List[Boolean] // not currently collected + ) diff --git a/compiler/src/dotty/tools/dotc/transform/linker/summaries/MethodSummaryBuilder.scala b/compiler/src/dotty/tools/dotc/transform/linker/summaries/MethodSummaryBuilder.scala new file mode 100644 index 000000000000..219c44c8ccca --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/summaries/MethodSummaryBuilder.scala @@ -0,0 +1,37 @@ +package dotty.tools.dotc.transform.linker.summaries + +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.transform.linker.types.ClosureType + +import scala.collection.mutable + +class MethodSummaryBuilder(val methodDef: Symbol, argumentStoredToHeap: List[Boolean]) { + + private val methodsCalled: mutable.Map[Type, List[CallInfo]] = mutable.Map.empty + private val argumentReturned: Byte = -1 // -1 if not known + + private var thisAccessed: Boolean = false + private var accessedModules: List[Symbol] = Nil + + private var definedClosures: List[ClosureType] = Nil + + def setThisAccessed(b: Boolean): Unit = { + thisAccessed = b + } + + def addAccessedModules(sym: Symbol): Unit = { + accessedModules = sym :: accessedModules + } + + def addMethodsCalledBy(tpe: Type, methods: List[CallInfo]): Unit = { + methodsCalled(tpe) = methods ::: methodsCalled.getOrElse(tpe, Nil) + } + + def addDefinedClosure(closure: ClosureType): Unit = { + definedClosures = closure :: definedClosures + } + + def result(): MethodSummary = + new MethodSummary(methodDef, thisAccessed, methodsCalled.toMap, definedClosures, accessedModules, argumentReturned, argumentStoredToHeap) +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/summaries/TastySummaries.scala b/compiler/src/dotty/tools/dotc/transform/linker/summaries/TastySummaries.scala new file mode 100644 index 000000000000..f22310337fd3 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/summaries/TastySummaries.scala @@ -0,0 +1,223 @@ +package dotty.tools.dotc.transform.linker.summaries + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.tpd.{Literal, ref} +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols.{Symbol, defn} +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Types.{TermRef, Type} +import dotty.tools.dotc.core.tasty._ +import dotty.tools.dotc.transform.linker.types.{ClosureType, PreciseType} + +import scala.collection.mutable + + +class TastySummaries { + + private var noSummaryAvailable = Set[Symbol]() + + private var loadedMethodSummaries: Map[Symbol, MethodSummary] = Map.empty + + def apply(d: Symbol)(implicit ctx: Context): Option[MethodSummary] = { + if (noSummaryAvailable(d)) None + else { + if (!loadedMethodSummaries.contains(d)) { + val nw = retrieveSummary(d) + nw.foreach(ms => loadedMethodSummaries = loadedMethodSummaries.updated(ms.methodDef, ms)) + if (!loadedMethodSummaries.contains(d)) + noSummaryAvailable += d + } + loadedMethodSummaries.get(d) + } + } + + private def retrieveSummary(sym: Symbol)(implicit ctx: Context): List[MethodSummary] = { + val topDenot = sym.topLevelClass.denot.asClass + topDenot.dottyUnpickler.fold[List[MethodSummary]](Nil)(_.summaries) + } + +} + + +object TastySummaries { + + val version = 1 + + val sectionName = "MethodSummaries" + + private val argsFlagsSize = 2 + + def saveInTasty(methodSummaries: List[MethodSummary])(implicit ctx: Context): Unit = { + for (cls <- ctx.compilationUnit.picklers.keySet) { + val pickler = ctx.compilationUnit.picklers(cls) + + if (!ctx.scala2Mode) { + val buf = new TastyBuffer(5000) + val treePickl = pickler.treePkl + val anchorTree = tpd.SyntheticValDef(sectionName.toTermName, Literal(Constant(sectionName))) + + treePickl.preRegister(anchorTree) + treePickl.pickle(anchorTree :: Nil) + + pickler.newSection(sectionName, buf) + val start = treePickl.buf.currentAddr + + val methods = methodSummaries.filter(_.methodDef.topLevelClass == cls) + + new SummaryWriter(treePickl, buf).write(methods) + + val sz = treePickl.buf.currentAddr.index - start.index + + ctx.debuglog("new section for " + cls + " size:" + + sz + "/" + buf.currentAddr.index + " increased by " + (sz + buf.length) * 1.0 / start.index) + // note: this is huge overestimate. This section contains a lot of refferences to already existing symbols and types + // and will be compressed during bytecode generation by TreePickler.compactify + + } + + pickler.treePkl.compactify() + } + } + + class SummaryReader(tReader: SummariesTreeUnpickler#TreeReader, reader: TastyReader)(implicit ctx: Context) { + + def read(): List[MethodSummary] = { + val version = reader.readInt() + val methodsSz = reader.readInt() + List.fill(methodsSz)(readMethodSummary(tReader, reader)) + } + + private def readSymbolRef = { + val sym = tReader.readType() + sym.termSymbol.orElse(sym.typeSymbol).orElse(sym.classSymbol) + } + + private def readMethodSummary(tReader: SummariesTreeUnpickler#TreeReader, reader: TastyReader): MethodSummary = { + val sym = readSymbolRef + val methodsSz = reader.readInt() + + val methodsCalled = new mutable.HashMap[Type, List[CallInfo]]() + + for (_ <- 0 until methodsSz) { + val preciseReceiver = reader.readByte() == 1 // TODO use a single compact bits section for all receivers before the loop + val receiver = if (preciseReceiver) new PreciseType(tReader.readType()) else tReader.readType() + + val listSz = reader.readInt() + methodsCalled(receiver) = List.fill(listSz)(readCallInfo) + } + + val accessedModulesSz = reader.readInt() + val accessedModules = List.fill(accessedModulesSz)(readSymbolRef) + + val argumentReturned = reader.readByte() + + val bitsExpected = reader.readByte() + val (thisAccessed :: argumentStoredToHeap) = readCompactBits(reader, bitsExpected) + + val definedClosures = Nil // TODO + + new MethodSummary(sym, thisAccessed, methodsCalled.toMap, definedClosures, accessedModules, argumentReturned.toByte, argumentStoredToHeap.take(bitsExpected - 1)) + } + + private def readCallInfo: CallInfo = { + val call = tReader.readType() + + val targsSz = reader.readByte() + val targs = List.fill(targsSz)(tReader.readType()) + + val argsSz = reader.readByte() + val argsFlags = readCompactBits(reader, argsFlagsSize * argsSz) + val argsPassed = argsFlags.sliding(argsFlagsSize, argsFlagsSize).map(readArg).toList + + val source = None // TODO + + CallInfo(call.asInstanceOf[TermRef], targs, argsPassed, source) // TODO no need to normalize the types + } + + private def readArg(argFlags: List[Boolean]): Type = { + assert(argFlags.length == argsFlagsSize) + val precise :: closure :: _ = argFlags + if (precise) new PreciseType(tReader.readType()) + else if (closure) new ClosureType(tReader.readTpt().asInstanceOf[tpd.Closure], tReader.readType(), readSymbolRef) + else tReader.readType() + } + + private def readCompactBits(reader: TastyReader, bitsExpected: Int): List[Boolean] = { + val bytesExpected = bitsExpected / 8 + (if (bitsExpected % 8 > 0) 1 else 0) + val bytes = reader.readBytes(bytesExpected) + bytes.toIterator.flatMap { bt => + List((bt & 1) != 0, (bt & 2) != 0, (bt & 4) != 0, (bt & 8) != 0, + (bt & 16) != 0, (bt & 32) != 0, (bt & 64) != 0, (bt & 128) != 0) + }.take(bitsExpected).toList + } + } + + private class SummaryWriter(treePickl: TreePickler, buf: TastyBuffer)(implicit ctx: Context) { + def write(methods: List[MethodSummary]) = { + buf.writeInt(version) //1 + buf.writeInt(methods.length) // 19 + methods.foreach(serializeMethodSummary) + } + + private def writeSymbolRef(sym: Symbol) = + treePickl.pickleType(ref(sym).tpe) + + private def serializeMethodSummary(ms: MethodSummary) = { + writeSymbolRef(ms.methodDef) //26 + + buf.writeInt(ms.methodsCalled.size) //29 + for ((receiver, methods) <- ms.methodsCalled) { + // TODO use a single compact bits section for all receivers before the loop + buf.writeByte(if (receiver.isInstanceOf[PreciseType]) 1 else 0) + val rec = receiver match { + case receiver: PreciseType => receiver.underlying + case _ => receiver + } + treePickl.pickleType(rec) + buf.writeInt(methods.size) + + methods.foreach(writeCallInfo) + } + + buf.writeInt(ms.accessedModules.length) + ms.accessedModules foreach writeSymbolRef + + buf.writeByte(ms.argumentReturned) + val flags = ms.thisAccessed :: ms.argumentStoredToHeap + buf.writeByte(flags.length) + writeCompactBits(flags) + } + + private def writeCallInfo(c: CallInfo): Unit = { + treePickl.pickleType(c.call) + + buf.writeByte(c.targs.size) + c.targs.foreach(targ => treePickl.pickleType(targ)) + + buf.writeByte(c.argumentsPassed.size) + val argFlags = + c.argumentsPassed.flatMap(arg => List(arg.isInstanceOf[PreciseType], arg.isInstanceOf[ClosureType])) + assert(argFlags.size == argsFlagsSize * c.argumentsPassed.size) + writeCompactBits(argFlags) + c.argumentsPassed.foreach(writeArg) + } + + private def writeArg(arg: Type): Unit = arg match { + case arg: PreciseType => + treePickl.pickleType(arg.underlying) + case arg: ClosureType => + treePickl.pickleTree(arg.meth) + treePickl.pickleType(arg.u) + writeSymbolRef(arg.implementedMethod) + case arg => + treePickl.pickleType(arg) + } + + private def writeCompactBits(ls: List[Boolean]): Unit = { + def foldByte(bt: List[Boolean]) = bt.foldRight(0)((bl: Boolean, acc: Int) => (if (bl) 1 else 0) + (acc << 1)) + ls.grouped(8).map(foldByte).foreach(buf.writeByte) + } + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/types/ClosureType.scala b/compiler/src/dotty/tools/dotc/transform/linker/types/ClosureType.scala new file mode 100644 index 000000000000..4687d7632fdf --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/types/ClosureType.scala @@ -0,0 +1,30 @@ +package dotty.tools.dotc.transform.linker.types + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Types.{SingletonType, Type} + +class ClosureType(val meth: tpd.Closure, val u: Type, val implementedMethod: Symbol)(implicit ctx: Context) extends SingletonType { + + /** The type to which this proxy forwards operations. */ + def underlying(implicit ctx: Context): Type = u + + /** customized hash code of this type. + * NotCached for uncached types. Cached types + * compute hash and use it as the type's hashCode. + */ + override val hash: Int = implementedMethod.hashCode() + meth.meth.symbol.hashCode() + + override def hashCode() = hash + + override def equals(other: Any): Boolean = other match { + case that: ClosureType => + meth == that.meth && + u == that.u && + implementedMethod == that.implementedMethod + case _ => false + } + + override def toString: String = s"ClosureType($meth, $u, $implementedMethod)" +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/types/ErazedType.scala b/compiler/src/dotty/tools/dotc/transform/linker/types/ErazedType.scala new file mode 100644 index 000000000000..68d98e123d59 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/types/ErazedType.scala @@ -0,0 +1,9 @@ +package dotty.tools.dotc.transform.linker.types + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Types.{Type, UncachedProxyType} + +class ErazedType extends UncachedProxyType { + /** The type to which this proxy forwards operations. */ + def underlying(implicit ctx: Context): Type = ctx.definitions.AnyType +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/types/JavaAllocatedType.scala b/compiler/src/dotty/tools/dotc/transform/linker/types/JavaAllocatedType.scala new file mode 100644 index 000000000000..b42485ad3324 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/types/JavaAllocatedType.scala @@ -0,0 +1,34 @@ +package dotty.tools.dotc.transform.linker.types + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Hashable +import dotty.tools.dotc.core.Types.{PolyType, SingletonType, Type, TypeParamRef} + +class JavaAllocatedType(private val u: Type)(implicit ctx: Context) extends SingletonType { + + assert(!u.widenDealias.isInstanceOf[JavaAllocatedType], u.widenDealias) + assert(!u.widenDealias.isInstanceOf[TypeParamRef], u.widenDealias) + assert(!u.widenDealias.isInstanceOf[PolyType], u.widenDealias) + + /** customized hash code of this type. + * NotCached for uncached types. Cached types + * compute hash and use it as the type's hashCode. + */ + def hash: Int = { + val underlying = u.hash + if (underlying == Hashable.NotCached) Hashable.NotCached + else if (underlying == Hashable.NotCached - 1) underlying + else underlying + 1 + } + + override def hashCode(): Int = hash + + def underlying(implicit ctx: Context): Type = u + + override def equals(other: Any): Boolean = other match { + case that: JavaAllocatedType => this.u == that.u + case _ => false + } + + override def toString: String = s"JavaAllocatedType($u)" +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/types/PreciseType.scala b/compiler/src/dotty/tools/dotc/transform/linker/types/PreciseType.scala new file mode 100644 index 000000000000..78b00f94dea0 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/types/PreciseType.scala @@ -0,0 +1,30 @@ +package dotty.tools.dotc.transform.linker.types + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Hashable +import dotty.tools.dotc.core.Types.{SingletonType, Type} + +class PreciseType(private[PreciseType] val u: Type) extends SingletonType { + + /** customized hash code of this type. + * NotCached for uncached types. Cached types + * compute hash and use it as the type's hashCode. + */ + def hash: Int = { + val underlying = u.hash + if (underlying == Hashable.NotCached) Hashable.NotCached + else if (underlying == Hashable.NotCached - 1) underlying + else underlying + 1 + } + + override def hashCode(): Int = hash + + def underlying(implicit ctx: Context): Type = u + + override def equals(other: Any): Boolean = other match { + case that: PreciseType => this.u == that.u + case _ => false + } + + override def toString: String = s"PreciseType($u)" +} diff --git a/compiler/src/dotty/tools/dotc/transform/linker/types/TypeNormalizer.scala b/compiler/src/dotty/tools/dotc/transform/linker/types/TypeNormalizer.scala new file mode 100644 index 000000000000..92747d68851b --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/linker/types/TypeNormalizer.scala @@ -0,0 +1,25 @@ +package dotty.tools.dotc.transform.linker.types + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Types._ + +class TypeNormalizer(implicit ctx: Context) extends TypeMap { + + override def apply(tp: Type): Type = tp match { + case tp: TypeRef if tp.typeSymbol.isClass && tp.typeSymbol.isStatic => + val sym = tp.typeSymbol + NamedType(sym.owner.thisType, sym.name, sym.denot) + + case tp: RefinedType => + def listRefinements(tp: RefinedType, acc: List[(Name, Type)]): (Type, List[(Name, Type)]) = tp.parent match { + case p: RefinedType => listRefinements(p, (tp.refinedName, tp.refinedInfo) :: acc) + case p => (p, (tp.refinedName, tp.refinedInfo) :: acc) + } + val (parent, refinements) = listRefinements(tp, Nil) + val normalizedParent = apply(parent) + refinements.sortBy(_._1.toString).foldLeft[Type](normalizedParent)((acc, x) => RefinedType(acc, x._1, x._2)) + + case _ => mapOver(tp) + } +} diff --git a/compiler/test/dotc/dotty-library.whitelist b/compiler/test/dotc/dotty-library.whitelist new file mode 100644 index 000000000000..7caae78a221b --- /dev/null +++ b/compiler/test/dotc/dotty-library.whitelist @@ -0,0 +1,11 @@ +scala/Product0.scala +scala/Eq.scala +scala/FunctionXXL.scala + +dotty/DottyPredef.scala +dotty/runtime/Arrays.scala +dotty/runtime/DeadCodeEliminated.scala +dotty/runtime/LazyHolders.scala +dotty/runtime/LazyVals.scala +#dotty/runtime/LegacyApp.scala +dotty/runtime/vc/VCPrototype.scala diff --git a/compiler/test/dotc/scala-collections.blacklist b/compiler/test/dotc/scala-collections.blacklist index d6972a645ed3..8979868aeb21 100644 --- a/compiler/test/dotc/scala-collections.blacklist +++ b/compiler/test/dotc/scala-collections.blacklist @@ -20,7 +20,8 @@ scala/collection/parallel/ParMap.scala scala/collection/parallel/ParMapLike.scala # -Yno-deep-subtypes fails - +scala/sys/process/ProcessImpl.scala +# Breaks in NameKinds ## Ycheck failures, presumably linked to TailCalls diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 8af721ca6cb5..08d7d2323fe1 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -5,6 +5,7 @@ package dotc import org.junit.{ Test, BeforeClass, AfterClass } import java.nio.file._ +import dotty.tools.io.JFile import java.util.stream.{ Stream => JStream } import scala.collection.JavaConverters._ import scala.util.matching.Regex @@ -28,8 +29,6 @@ class CompilationTests extends ParallelTesting { // Positive tests ------------------------------------------------------------ @Test def compilePos: Unit = { - compileList("compileStdLib", StdLibSources.whitelisted, scala2Mode.and("-migration", "-Yno-inline")) + - compileDir("../collection-strawman/src/main", defaultOptions) + compileDir("../compiler/src/dotty/tools/dotc/ast", defaultOptions) + compileDir("../compiler/src/dotty/tools/dotc/config", defaultOptions) + compileDir("../compiler/src/dotty/tools/dotc/core", allowDeepSubtypes) + @@ -295,15 +294,83 @@ class CompilationTests extends ParallelTesting { tests.delete() } + @Test def linkAll: Unit = { + def commonLibraries = { + compileList("stdlib", StdLibSources.whitelisted, scala2Mode.and("-migration", "-Yno-inline")) + + compileList("strawman", strawmanSources, defaultOptions) + } - private val (compilerSources, backendSources, backendJvmSources) = { - def sources(paths: JStream[Path], excludedFiles: List[String] = Nil): List[String] = - paths.iterator().asScala - .filter(path => - (path.toString.endsWith(".scala") || path.toString.endsWith(".java")) - && !excludedFiles.contains(path.getFileName.toString)) - .map(_.toString).toList + // Compile and setup libraries + + val libraries = commonLibraries.keepOutput.checkCompile() + + val commonLibrariesPath = defaultOutputDir + "commonLibraries/" + + val utilsOnlyClassPath = mkClassPath(Jars.dottyTestDeps) + val stdlibClassPath = mkClassPath(commonLibrariesPath + "stdlib" :: Jars.dottyTestDeps) + val strawmanClassPath = mkClassPath(commonLibrariesPath + "strawman" :: Jars.dottyTestDeps) + + def linkTests(dir: String, flags: Array[String], nameSuffix: String = ""): CompilationTest = { + val testsDir = new JFile(dir) + val tests = for (test <- testsDir.listFiles() if test.isDirectory || test.getName.endsWith(".scala")) yield { + val name = if (test.isDirectory) test.getName else test.getName.dropRight(6) + val files0 = sources(Files.walk(testsDir.toPath)) + val files = if (test.isDirectory) files0 else test.getAbsolutePath :: Nil + compileList(name, files, flags, testsDir.getName + nameSuffix) + } + assert(tests.nonEmpty) + if (tests.length == 1) tests.head + else tests.reduce((a, b) => a + b) + } + + // DCE tests + + def linkDCETests = compileFilesInDir("../tests/link/dce", linkDCE ++ classPath) + def linkAggressiveDCETests = compileFilesInDir("../tests/link/dce", linkAggressiveDCE ++ classPath) + def linkStrawmanDCETests = linkTests("../tests/link/strawman-dce", linkDCE ++ strawmanClassPath) + def linkStrawmanAggressiveDCETests = linkTests("../tests/link/strawman-dce", linkAggressiveDCE ++ strawmanClassPath, "-aggressive") + def linkStdLibDCE = { + val testsDir = new JFile("../tests/link/stdlib-dce") + val tests = for (test <- testsDir.listFiles() if test.isDirectory) yield { + val files = sources(Files.walk(test.toPath)) + compileList(test.getName, files, scala2Mode ++ linkDCE ++ stdlibClassPath) + } + tests.reduce((a, b) => a + b) + } + + // specialization tests + + def linkSpecializeTests = linkTests("../tests/link/specialize", linkSpecialize ++ utilsOnlyClassPath) + def linkStrawmanSpecializeTests = linkTests("../tests/link/strawman-specialize", linkSpecialize ++ strawmanClassPath) + + // Plain strawman tests + + def strawmanTest = compileFilesInDir("../tests/strawman", basicDefaultOptions ++ strawmanClassPath) + + // Run all tests + + { + linkDCETests + + linkAggressiveDCETests + + /* linkStrawmanDCETests + + linkStrawmanAggressiveDCETests + */ + linkStdLibDCE + + linkSpecializeTests /* + + linkStrawmanSpecializeTests + + strawmanTest */ + }.checkRuns() + + // libraries.delete() + } + + private def strawmanSources = + sources(Files.walk(Paths.get("../collection-strawman/src/main"))) + + private def testUtils = + sources(Files.walk(Paths.get("../tests/utils/"))) + + private val (compilerSources, backendSources, backendJvmSources) = { val compilerDir = Paths.get("../compiler/src") val compilerSources0 = sources(Files.walk(compilerDir)) @@ -323,6 +390,13 @@ class CompilationTests extends ParallelTesting { (compilerSources0, backendSources0, backendJvmSources0) } + + private def sources(paths: JStream[Path], excludedFiles: List[String] = Nil): List[String] = + paths.iterator().asScala + .filter(path => + (path.toString.endsWith(".scala") || path.toString.endsWith(".java")) + && !excludedFiles.contains(path.getFileName.toString)) + .map(_.toString).toList } object CompilationTests { diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index dacc5646d3c4..6e5920c122e4 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -1079,8 +1079,7 @@ trait ParallelTesting extends RunnerOrchestration { self => * `testName` since files can be in separate directories and or be otherwise * dissociated */ - def compileList(testName: String, files: List[String], flags: Array[String])(implicit outDirectory: String): CompilationTest = { - val callingMethod = getCallingMethod + def compileList(testName: String, files: List[String], flags: Array[String], callingMethod: String = getCallingMethod)(implicit outDirectory: String): CompilationTest = { val outDir = outDirectory + callingMethod + "/" + testName + "/" // Directories in which to compile all containing files with `flags`: diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 440a0a629534..f0e8d55ba3ab 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -23,8 +23,10 @@ object TestConfiguration { "-Yforce-sbt-phases" ) - val classPath = { - val paths = Jars.dottyTestDeps map { p => + val classPath = mkClassPath(Jars.dottyTestDeps) + + def mkClassPath(classPaths: List[String]): Array[String] = { + val paths = classPaths map { p => val file = new java.io.File(p) assert( file.exists, @@ -52,7 +54,8 @@ object TestConfiguration { private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,mixin,arrayConstructors,labelDef") - val defaultUnoptimised = noCheckOptions ++ checkOptions ++ yCheckOptions ++ classPath + val basicDefaultOptions = noCheckOptions ++ checkOptions ++ yCheckOptions + val defaultUnoptimised = basicDefaultOptions ++ classPath val defaultOptions = defaultUnoptimised :+ "-optimise" val allowDeepSubtypes = defaultOptions diff Array("-Yno-deep-subtypes") val allowDoubleBindings = defaultOptions diff Array("-Yno-double-bindings") @@ -64,4 +67,13 @@ object TestConfiguration { val scala2Mode = defaultOptions ++ Array("-language:Scala2") val explicitUTF8 = defaultOptions ++ Array("-encoding", "UTF8") val explicitUTF16 = defaultOptions ++ Array("-encoding", "UTF16") + + val stdlibMode = scala2Mode.and("-migration", "-Yno-inline") + val linkStdlibMode = stdlibMode.and("-Ylink-stdlib") + + val linkCommon = Array("-link-vis", "-Ylog:callGraph") ++ basicDefaultOptions + val linkDCEcommon = Array("-link-java-conservative", "-Ylink-dce-checks") ++ linkCommon + val linkDCE = "-link-dce" +: linkDCEcommon + val linkAggressiveDCE = "-link-aggressive-dce" +: linkDCEcommon + val linkSpecialize = Array("-link-specialize", "-Ylog:specializeClass,specializeClassParents") ++ linkCommon } diff --git a/library/src/dotty/runtime/DeadCodeEliminated.scala b/library/src/dotty/runtime/DeadCodeEliminated.scala new file mode 100644 index 000000000000..04df43074d1f --- /dev/null +++ b/library/src/dotty/runtime/DeadCodeEliminated.scala @@ -0,0 +1,3 @@ +package dotty.runtime + +class DeadCodeEliminated extends RuntimeException diff --git a/library/src/scala/EntryPoint.scala b/library/src/scala/EntryPoint.scala new file mode 100644 index 000000000000..0bb8a62ba71e --- /dev/null +++ b/library/src/scala/EntryPoint.scala @@ -0,0 +1,3 @@ +package scala + +final class EntryPoint extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/internal/link/AssertNotReachable.scala b/library/src/scala/annotation/internal/link/AssertNotReachable.scala new file mode 100644 index 000000000000..8849f8dfa8d3 --- /dev/null +++ b/library/src/scala/annotation/internal/link/AssertNotReachable.scala @@ -0,0 +1,8 @@ +package scala.annotation.internal.link + +/** Assert at compile time that the symbol of a method is not reachable. + * + * This does not work on methods that get created later like getters, + * setters, bridges, ... + */ +final class AssertNotReachable extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/internal/link/AssertReachable.scala b/library/src/scala/annotation/internal/link/AssertReachable.scala new file mode 100644 index 000000000000..441f0618116a --- /dev/null +++ b/library/src/scala/annotation/internal/link/AssertReachable.scala @@ -0,0 +1,8 @@ +package scala.annotation.internal.link + +/** Assert at compile time that the symbol of a method is reachable. + * + * This does not work on methods that get created later like getters, + * setters, bridges, ... + */ +final class AssertReachable extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/internal/link/CallGraphBounds.scala b/library/src/scala/annotation/internal/link/CallGraphBounds.scala new file mode 100644 index 000000000000..063bb8adc644 --- /dev/null +++ b/library/src/scala/annotation/internal/link/CallGraphBounds.scala @@ -0,0 +1,3 @@ +package scala.annotation.internal.link + +final class CallGraphBounds(reachableClasses: Int, classesWithReachableMethods: Int, reachableMethods: Int) extends scala.annotation.StaticAnnotation diff --git a/library/src/scala/annotation/internal/link/DoNotDeadCodeEliminate.scala b/library/src/scala/annotation/internal/link/DoNotDeadCodeEliminate.scala new file mode 100644 index 000000000000..54dc14cd6acd --- /dev/null +++ b/library/src/scala/annotation/internal/link/DoNotDeadCodeEliminate.scala @@ -0,0 +1,4 @@ +package scala.annotation.internal.link + +/** Ignores dead code elimination of this method */ +final class DoNotDeadCodeEliminate extends scala.annotation.StaticAnnotation diff --git a/project/Build.scala b/project/Build.scala index fea9b5496547..c2827e5c53bc 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -229,6 +229,13 @@ object Build { libraryDependencies := Seq() ) + lazy val linkBootstrappedSettings = Seq( + scalacOptions ++= Seq( + "-link-vis", + "-Ylog:callGraph" + ) + ) + /** Projects -------------------------------------------------------------- */ // Needed because the dotty project aggregates dotty-sbt-bridge but dotty-sbt-bridge @@ -636,6 +643,7 @@ object Build { dependsOn(`dotty-interfaces`). dependsOn(`dotty-library-bootstrapped`). settings(commonBootstrappedSettings). + // settings(linkBootstrappedSettings). settings(dottyCompilerSettings). settings( packageAll := { diff --git a/tests/link/dce-failing/link-closure-generic-context-1.check b/tests/link/dce-failing/link-closure-generic-context-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce-failing/link-closure-generic-context-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce-failing/link-closure-generic-context-1.scala b/tests/link/dce-failing/link-closure-generic-context-1.scala new file mode 100644 index 000000000000..b2dd99e56b88 --- /dev/null +++ b/tests/link/dce-failing/link-closure-generic-context-1.scala @@ -0,0 +1,10 @@ +import scala.annotation.internal + +object Test { + class A[T] { + def f(y: T): T = ((x: T) => x)(y) + } + + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 8, reachableMethods = 9) + def main(args: Array[String]): Unit = System.out.println((new A[Int]).f(42)) +} \ No newline at end of file diff --git a/tests/link/dce-failing/link-closure-generic-context-2.check b/tests/link/dce-failing/link-closure-generic-context-2.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce-failing/link-closure-generic-context-2.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce-failing/link-closure-generic-context-2.scala b/tests/link/dce-failing/link-closure-generic-context-2.scala new file mode 100644 index 000000000000..ec530c9f80c3 --- /dev/null +++ b/tests/link/dce-failing/link-closure-generic-context-2.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + class A[T] { + def f(y: T): T = ((x: T) => x)(y) + } + + class B[T] extends A[T] + + // @internal.link.CallGraphBounds(reachableClasses = 1, classesWithReachableMethods = 1, reachableMethods = 1) + def main(args: Array[String]): Unit = System.out.println((new B[Int]).f(42)) +} \ No newline at end of file diff --git a/tests/link/dce-failing/link-code-from-java-1.scala b/tests/link/dce-failing/link-code-from-java-1.scala new file mode 100644 index 000000000000..79761b6b62a5 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-1.scala @@ -0,0 +1,82 @@ +import java.io.File +import java.io.FileFilter +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.lang.reflect.Type + +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 139, classesWithReachableMethods = 23, reachableMethods = 85) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val a = new A + Test.shouldDCE(a.f1) + a.hashCode() + a.equals(a) + a.toString + + val b = new B {} + Test.shouldDCE(b.f1) + b.hashCode() + b.equals(b) + b.toString + + val c = new C {} + Test.shouldDCE(c.f1) + c.hashCode() + c.equals(c) + c.toString + } + + @EntryPoint def entryPoint(): Unit = { + System.out.println(new A) + System.out.println(new B {}) + System.out.println(new C {}) + } +} + +class A { + def f1 = 42 + override def hashCode(): Int = super.hashCode() + override def equals(obj: scala.Any): Boolean = super.equals(obj) + override def toString: String = super.toString +} + +abstract class B { + def f1 = 42 + override def hashCode(): Int = super.hashCode() + override def equals(obj: scala.Any): Boolean = super.equals(obj) + override def toString: String = super.toString +} + +trait C { + def f1 = 42 + override def hashCode(): Int = super.hashCode() + override def equals(obj: scala.Any): Boolean = super.equals(obj) + override def toString: String = super.toString +} diff --git a/tests/link/dce-failing/link-code-from-java-2-c.check b/tests/link/dce-failing/link-code-from-java-2-c.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-2-c.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce-failing/link-code-from-java-2-c.scala b/tests/link/dce-failing/link-code-from-java-2-c.scala new file mode 100644 index 000000000000..2dc05b73a20b --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-2-c.scala @@ -0,0 +1,52 @@ +import java.util.Observable + +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 106, classesWithReachableMethods = 19, reachableMethods = 70) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val c = new C {} + Test.shouldDCE(c.f1) + c.hashCode() + c.equals(c) + c.toString + c.update(new Observable, null) + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new C {}) + } +} + +trait C extends java.util.Observer { + def f1 = 42 + override def hashCode(): Int = super.hashCode() + override def equals(obj: scala.Any): Boolean = super.equals(obj) + override def toString: String = super.toString + override def update(o: Observable, arg: scala.Any): Unit = () +} diff --git a/tests/link/dce-failing/link-code-from-java-3-a.check b/tests/link/dce-failing/link-code-from-java-3-a.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-3-a.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce-failing/link-code-from-java-3-a.scala b/tests/link/dce-failing/link-code-from-java-3-a.scala new file mode 100644 index 000000000000..b281568ad055 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-3-a.scala @@ -0,0 +1,54 @@ +import java.util.Observable + +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 1, classesWithReachableMethods = 1, reachableMethods = 1) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val a = new A[AA] + a.hasNext + a.next() + val aa = new AA + Test.shouldDCE(aa.f) + aa.toString + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new A[AA]) + } +} + +class AA { + def f = 42 + override def toString: String = super.toString +} + +class A[E] extends java.util.Iterator[E] { + override def next(): E = null.asInstanceOf[E] + override def hasNext: Boolean = false +} diff --git a/tests/link/dce-failing/link-code-from-java-3-b.check b/tests/link/dce-failing/link-code-from-java-3-b.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-3-b.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce-failing/link-code-from-java-3-b.scala b/tests/link/dce-failing/link-code-from-java-3-b.scala new file mode 100644 index 000000000000..25823bbf3c47 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-3-b.scala @@ -0,0 +1,54 @@ +import java.util.Observable + +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 1, classesWithReachableMethods = 1, reachableMethods = 1) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val b = new B[BB] {} + b.hasNext + b.next() + val bb = new BB + Test.shouldDCE(bb.f) + bb.toString + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new B[BB] {}) + } +} + +class BB { + def f = 43 + override def toString: String = super.toString +} + +abstract class B[E] extends java.util.Iterator[E] { + override def next(): E = null.asInstanceOf[E] + override def hasNext(): Boolean = false +} diff --git a/tests/link/dce-failing/link-code-from-java-3-c.check b/tests/link/dce-failing/link-code-from-java-3-c.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-3-c.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce-failing/link-code-from-java-3-c.scala b/tests/link/dce-failing/link-code-from-java-3-c.scala new file mode 100644 index 000000000000..8b14750a84ac --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-3-c.scala @@ -0,0 +1,54 @@ +import java.util.Observable + +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 1, classesWithReachableMethods = 1, reachableMethods = 1) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val c = new C[CC] {} + c.hasNext + c.next() + val cc = new CC + Test.shouldDCE(cc.f) + cc.toString + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new C[CC] {}) + } +} + +class CC { + def f = 44 + override def toString: String = super.toString +} + +trait C[E] extends java.util.Iterator[E] { + override def next(): E = null.asInstanceOf[E] + override def hasNext(): Boolean = false +} diff --git a/tests/link/dce-failing/link-code-from-java-4.scala b/tests/link/dce-failing/link-code-from-java-4.scala new file mode 100644 index 000000000000..92c25c121e15 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-4.scala @@ -0,0 +1,89 @@ +import java.util +import java.util.Observable + +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 1, classesWithReachableMethods = 1, reachableMethods = 1) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + val coll = new Coll[AA] + coll.removeAll(coll) + coll.retainAll(coll) + coll.clear() + coll.toArray + coll.toArray(new Array[AA](0)) + coll.size() + coll.remove(new AA) + coll.contains(new AA) + coll.addAll(coll) + coll.iterator() + coll.isEmpty + coll.containsAll(coll) + coll.add(new AA) + + val aa = new AA + Test.shouldDCE(aa.f) + aa.toString + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new Coll[AA]) + } +} + +class AA { + def f = 42 + override def toString: String = super.toString +} + +class Coll[E] extends java.util.Collection[E] { + def removeAll(c: util.Collection[_]): Boolean = false + + def retainAll(c: util.Collection[_]): Boolean = false + + def clear(): Unit = () + + def toArray: Array[Object] = null + + // FIXME cannot compile due to issue #1747 + def toArray[T](a: Array[T]): Array[T] = null + + def size(): Int = 0 + + def remove(o: scala.Any): Boolean = false + + def contains(o: scala.Any): Boolean = false + + def addAll(c: util.Collection[_ <: E]): Boolean = false + + def iterator(): util.Iterator[E] = null + + def isEmpty: Boolean = false + + def containsAll(c: util.Collection[_]): Boolean = false + + def add(e: E): Boolean = false +} diff --git a/tests/link/dce-failing/link-code-from-java-5.check b/tests/link/dce-failing/link-code-from-java-5.check new file mode 100644 index 000000000000..f70d7bba4ae1 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-5.check @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/tests/link/dce-failing/link-code-from-java-5.scala b/tests/link/dce-failing/link-code-from-java-5.scala new file mode 100644 index 000000000000..3bf642a0d217 --- /dev/null +++ b/tests/link/dce-failing/link-code-from-java-5.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal +import scala.math.Ordering + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 91, classesWithReachableMethods = 24, reachableMethods = 93) + def main(args: Array[String]): Unit = { + val arr = new Array[Object](1) + arr(0) = 42 + java.util.Arrays.sort(arr, Ordering.Int.asInstanceOf[Ordering[Object]]) // extracted from SeqLike.sorted + System.out.println(arr(0)) + } +} diff --git a/tests/link/dce-failing/link-java-polimorphic-method-3.check b/tests/link/dce-failing/link-java-polimorphic-method-3.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce-failing/link-java-polimorphic-method-3.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce-failing/link-java-polimorphic-method-3/Bar.java b/tests/link/dce-failing/link-java-polimorphic-method-3/Bar.java new file mode 100644 index 000000000000..f48a7e143589 --- /dev/null +++ b/tests/link/dce-failing/link-java-polimorphic-method-3/Bar.java @@ -0,0 +1,4 @@ + +public class Bar { + public T get(); +} \ No newline at end of file diff --git a/tests/link/dce-failing/link-java-polimorphic-method-3/Foo.java b/tests/link/dce-failing/link-java-polimorphic-method-3/Foo.java new file mode 100644 index 000000000000..2ca7b6ce395c --- /dev/null +++ b/tests/link/dce-failing/link-java-polimorphic-method-3/Foo.java @@ -0,0 +1,10 @@ + +public class Foo { + public static String foo(Bar bar) { + return bar.get.string + } + + public String string() { + return "42"; + } +} \ No newline at end of file diff --git a/tests/link/dce-failing/link-java-polimorphic-method-3/Test.scala b/tests/link/dce-failing/link-java-polimorphic-method-3/Test.scala new file mode 100644 index 000000000000..556b78789b5c --- /dev/null +++ b/tests/link/dce-failing/link-java-polimorphic-method-3/Test.scala @@ -0,0 +1,15 @@ +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 40, classesWithReachableMethods = 7, reachableMethods = 55) + def main(args: Array[String]): Unit = { + System.out.println(new Baz) + } +} + +class Baz extends Bar[Foo] { + + def get(): Foo = new Foo + + override def toString(): String = Foo.foo(this) +} \ No newline at end of file diff --git a/tests/link/dce-failing/link-predef-toList/MainGenericRunner.java b/tests/link/dce-failing/link-predef-toList/MainGenericRunner.java new file mode 100644 index 000000000000..ac1b4feaeb3a --- /dev/null +++ b/tests/link/dce-failing/link-predef-toList/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex); + } + } +} diff --git a/tests/link/dce-failing/link-predef-toList/Predef.scala b/tests/link/dce-failing/link-predef-toList/Predef.scala new file mode 100644 index 000000000000..536952779ea0 --- /dev/null +++ b/tests/link/dce-failing/link-predef-toList/Predef.scala @@ -0,0 +1,514 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic } +import immutable.StringOps +import mutable.ArrayOps +import generic.CanBuildFrom +import scala.annotation.{ elidable, implicitNotFound } +import scala.annotation.elidable.ASSERTION +import scala.language.{implicitConversions, existentials} +import scala.io.StdIn + +/** The `Predef` object provides definitions that are accessible in all Scala + * compilation units without explicit qualification. + * + * === Commonly Used Types === + * Predef provides type aliases for types which are commonly used, such as + * the immutable collection types [[scala.collection.immutable.Map]], + * [[scala.collection.immutable.Set]], and the [[scala.collection.immutable.List]] + * constructors ([[scala.collection.immutable.::]] and + * [[scala.collection.immutable.Nil]]). + * + * === Console I/O === + * Predef provides a number of simple functions for console I/O, such as + * `print`, `println`, `readLine`, `readInt`, etc. These functions are all + * aliases of the functions provided by [[scala.Console]]. + * + * === Assertions === + * + * A set of `assert` functions are provided for use as a way to document + * and dynamically check invariants in code. `assert` statements can be elided + * at runtime by providing the command line argument `-Xdisable-assertions` to + * the `scala` command. + * + * Variants of `assert` intended for use with static analysis tools are also + * provided: `assume`, `require` and `ensuring`. `require` and `ensuring` are + * intended for use as a means of design-by-contract style specification + * of pre- and post-conditions on functions, with the intention that these + * specifications could be consumed by a static analysis tool. For instance, + * + * {{{ + * def addNaturals(nats: List[Int]): Int = { + * require(nats forall (_ >= 0), "List contains negative numbers") + * nats.foldLeft(0)(_ + _) + * } ensuring(_ >= 0) + * }}} + * + * The declaration of `addNaturals` states that the list of integers passed should + * only contain natural numbers (i.e. non-negative), and that the result returned + * will also be natural. `require` is distinct from `assert` in that if the + * condition fails, then the caller of the function is to blame rather than a + * logical error having been made within `addNaturals` itself. `ensures` is a + * form of `assert` that declares the guarantee the function is providing with + * regards to it's return value. + * + * === Implicit Conversions === + * A number of commonly applied implicit conversions are also defined here, and + * in the parent type [[scala.LowPriorityImplicits]]. Implicit conversions + * are provided for the "widening" of numeric values, for instance, converting a + * Short value to a Long value as required, and to add additional higher-order + * functions to Array values. These are described in more detail in the documentation of [[scala.Array]]. + */ +object Predef extends LowPriorityImplicits with DeprecatedPredef { + + // ADDED TO TEST DCE + println("Predef loaded") + + /** + * Retrieve the runtime representation of a class type. `classOf[T]` is equivalent to + * the class literal `T.class` in Java. + * + * @example {{{ + * val listClass = classOf[List[_]] + * // listClass is java.lang.Class[List[_]] = class scala.collection.immutable.List + * + * val mapIntString = classOf[Map[Int,String]] + * // mapIntString is java.lang.Class[Map[Int,String]] = interface scala.collection.immutable.Map + * }}} + */ + def classOf[T]: Class[T] = null // This is a stub method. The actual implementation is filled in by the compiler. + + type String = java.lang.String + type Class[T] = java.lang.Class[T] + + // miscelleaneous ----------------------------------------------------- + scala.`package` // to force scala package object to be seen. + scala.collection.immutable.List // to force Nil, :: to be seen. + + type Function[-A, +B] = Function1[A, B] + + type Map[A, +B] = immutable.Map[A, B] + type Set[A] = immutable.Set[A] + val Map = immutable.Map + val Set = immutable.Set + + // Manifest types, companions, and incantations for summoning + @annotation.implicitNotFound(msg = "No ClassManifest available for ${T}.") + @deprecated("Use `scala.reflect.ClassTag` instead", "2.10.0") + type ClassManifest[T] = scala.reflect.ClassManifest[T] + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("This notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") + type OptManifest[T] = scala.reflect.OptManifest[T] + @annotation.implicitNotFound(msg = "No Manifest available for ${T}.") + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("Use `scala.reflect.ClassTag` (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") + type Manifest[T] = scala.reflect.Manifest[T] + @deprecated("Use `scala.reflect.ClassTag` instead", "2.10.0") + val ClassManifest = scala.reflect.ClassManifest + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("Use `scala.reflect.ClassTag` (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") + // val Manifest = scala.reflect.Manifest + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("This notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") + val NoManifest = scala.reflect.NoManifest + + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("Use scala.reflect.classTag[T] and scala.reflect.runtime.universe.typeTag[T] instead", "2.10.0") + def manifest[T](implicit m: Manifest[T]) = m + @deprecated("Use scala.reflect.classTag[T] instead", "2.10.0") + def classManifest[T](implicit m: ClassManifest[T]) = m + // TODO undeprecated until Scala reflection becomes non-experimental + // @deprecated("This notion doesn't have a corresponding concept in 2.10, because scala.reflect.runtime.universe.TypeTag can capture arbitrary types. Use type tags instead of manifests, and there will be no need in opt manifests.", "2.10.0") + def optManifest[T](implicit m: OptManifest[T]) = m + + // Minor variations on identity functions + def identity[A](x: A): A = x // @see `conforms` for the implicit version + @inline def implicitly[T](implicit e: T) = e // for summoning implicit values from the nether world -- TODO: when dependent method types are on by default, give this result type `e.type`, so that inliner has better chance of knowing which method to inline in calls like `implicitly[MatchingStrategy[Option]].zero` + @inline def locally[T](x: T): T = x // to communicate intent and avoid unmoored statements + + // errors and asserts ------------------------------------------------- + + // !!! Remove this when possible - ideally for 2.11. + // We are stuck with it a while longer because sbt's compiler interface + // still calls it as of 0.12.2. + @deprecated("Use `sys.error(message)` instead", "2.9.0") + def error(message: String): Nothing = sys.error(message) + + /** Tests an expression, throwing an `AssertionError` if false. + * Calls to this method will not be generated if `-Xelide-below` + * is at least `ASSERTION`. + * + * @see elidable + * @param assertion the expression to test + */ + @elidable(ASSERTION) + def assert(assertion: Boolean): Unit = { + if (!assertion) + throw new java.lang.AssertionError("assertion failed") + } + + /** Tests an expression, throwing an `AssertionError` if false. + * Calls to this method will not be generated if `-Xelide-below` + * is at least `ASSERTION`. + * + * @see elidable + * @param assertion the expression to test + * @param message a String to include in the failure message + */ + @elidable(ASSERTION) @inline + final def assert(assertion: Boolean, message: => Any): Unit = { + if (!assertion) + throw new java.lang.AssertionError("assertion failed: "+ message) + } + + /** Tests an expression, throwing an `AssertionError` if false. + * This method differs from assert only in the intent expressed: + * assert contains a predicate which needs to be proven, while + * assume contains an axiom for a static checker. Calls to this method + * will not be generated if `-Xelide-below` is at least `ASSERTION`. + * + * @see elidable + * @param assumption the expression to test + */ + @elidable(ASSERTION) + def assume(assumption: Boolean): Unit = { + if (!assumption) + throw new java.lang.AssertionError("assumption failed") + } + + /** Tests an expression, throwing an `AssertionError` if false. + * This method differs from assert only in the intent expressed: + * assert contains a predicate which needs to be proven, while + * assume contains an axiom for a static checker. Calls to this method + * will not be generated if `-Xelide-below` is at least `ASSERTION`. + * + * @see elidable + * @param assumption the expression to test + * @param message a String to include in the failure message + */ + @elidable(ASSERTION) @inline + final def assume(assumption: Boolean, message: => Any): Unit = { + if (!assumption) + throw new java.lang.AssertionError("assumption failed: "+ message) + } + + /** Tests an expression, throwing an `IllegalArgumentException` if false. + * This method is similar to `assert`, but blames the caller of the method + * for violating the condition. + * + * @param requirement the expression to test + */ + def require(requirement: Boolean): Unit = { + if (!requirement) + throw new IllegalArgumentException("requirement failed") + } + + /** Tests an expression, throwing an `IllegalArgumentException` if false. + * This method is similar to `assert`, but blames the caller of the method + * for violating the condition. + * + * @param requirement the expression to test + * @param message a String to include in the failure message + */ + @inline final def require(requirement: Boolean, message: => Any): Unit = { + if (!requirement) + throw new IllegalArgumentException("requirement failed: "+ message) + } + + /** `???` can be used for marking methods that remain to be implemented. + * @throws NotImplementedError + */ + def ??? : Nothing = throw new NotImplementedError + + // tupling ------------------------------------------------------------ + + @deprecated("Use built-in tuple syntax or Tuple2 instead", "2.11.0") + type Pair[+A, +B] = Tuple2[A, B] + @deprecated("Use built-in tuple syntax or Tuple2 instead", "2.11.0") + object Pair { + def apply[A, B](x: A, y: B) = Tuple2(x, y) + def unapply[A, B](x: Tuple2[A, B]): Option[Tuple2[A, B]] = Some(x) + } + + @deprecated("Use built-in tuple syntax or Tuple3 instead", "2.11.0") + type Triple[+A, +B, +C] = Tuple3[A, B, C] + @deprecated("Use built-in tuple syntax or Tuple3 instead", "2.11.0") + object Triple { + def apply[A, B, C](x: A, y: B, z: C) = Tuple3(x, y, z) + def unapply[A, B, C](x: Tuple3[A, B, C]): Option[Tuple3[A, B, C]] = Some(x) + } + + // implicit classes ----------------------------------------------------- + + implicit final class ArrowAssoc[A](private val self: A) extends AnyVal { + @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y) + def →[B](y: B): Tuple2[A, B] = ->(y) + } + + implicit final class Ensuring[A](private val self: A) extends AnyVal { + def ensuring(cond: Boolean): A = { assert(cond); self } + def ensuring(cond: Boolean, msg: => Any): A = { assert(cond, msg); self } + def ensuring(cond: A => Boolean): A = { assert(cond(self)); self } + def ensuring(cond: A => Boolean, msg: => Any): A = { assert(cond(self), msg); self } + } + + implicit final class StringFormat[A](private val self: A) extends AnyVal { + /** Returns string formatted according to given `format` string. + * Format strings are as for `String.format` + * (@see java.lang.String.format). + */ + @inline def formatted(fmtstr: String): String = fmtstr format self + } + + // TODO: remove, only needed for binary compatibility of 2.11.0-RC1 with 2.11.0-M8 + // note that `private[scala]` becomes `public` in bytecode + private[scala] final class StringAdd[A](private val self: A) extends AnyVal { + def +(other: String): String = String.valueOf(self) + other + } + private[scala] def StringAdd(x: Any): Any = new StringAdd(x) + + // SI-8229 retaining the pre 2.11 name for source compatibility in shadowing this implicit + implicit final class any2stringadd[A](private val self: A) extends AnyVal { + def +(other: String): String = String.valueOf(self) + other + } + + implicit final class RichException(private val self: Throwable) extends AnyVal { + import scala.compat.Platform.EOL + @deprecated("Use Throwable#getStackTrace", "2.11.0") def getStackTraceString = self.getStackTrace().mkString("", EOL, EOL) + } + + implicit final class SeqCharSequence(val __sequenceOfChars: scala.collection.IndexedSeq[Char]) extends CharSequence { + def length: Int = __sequenceOfChars.length + def charAt(index: Int): Char = __sequenceOfChars(index) + def subSequence(start: Int, end: Int): CharSequence = new SeqCharSequence(__sequenceOfChars.slice(start, end)) + override def toString = __sequenceOfChars mkString "" + } + + implicit final class ArrayCharSequence(val __arrayOfChars: Array[Char]) extends CharSequence { + def length: Int = __arrayOfChars.length + def charAt(index: Int): Char = __arrayOfChars(index) + def subSequence(start: Int, end: Int): CharSequence = new runtime.ArrayCharSequence(__arrayOfChars, start, end) + override def toString = __arrayOfChars mkString "" + } + + implicit val StringCanBuildFrom: CanBuildFrom[String, Char, String] = new CanBuildFrom[String, Char, String] { + def apply(from: String) = apply() + def apply() = mutable.StringBuilder.newBuilder + } + + @inline implicit def augmentString(x: String): StringOps = new StringOps(x) + @inline implicit def unaugmentString(x: StringOps): String = x.repr + + // printing ----------------------------------------------------------- + + def print(x: Any) = Console.print(x) + def println() = Console.println() + def println(x: Any) = Console.println(x) + def printf(text: String, xs: Any*) = Console.print(text.format(xs: _*)) + + // views -------------------------------------------------------------- + + implicit def tuple2ToZippedOps[T1, T2](x: (T1, T2)): runtime.Tuple2Zipped.Ops[T1, T2] = new runtime.Tuple2Zipped.Ops(x) + implicit def tuple3ToZippedOps[T1, T2, T3](x: (T1, T2, T3)): runtime.Tuple3Zipped.Ops[T1, T2, T3] = new runtime.Tuple3Zipped.Ops(x) + + implicit def genericArrayOps[T](xs: Array[T]): ArrayOps[T] = (xs match { + case x: Array[AnyRef] => refArrayOps[AnyRef](x) + case x: Array[Boolean] => booleanArrayOps(x) + case x: Array[Byte] => byteArrayOps(x) + case x: Array[Char] => charArrayOps(x) + case x: Array[Double] => doubleArrayOps(x) + case x: Array[Float] => floatArrayOps(x) + case x: Array[Int] => intArrayOps(x) + case x: Array[Long] => longArrayOps(x) + case x: Array[Short] => shortArrayOps(x) + case x: Array[Unit] => unitArrayOps(x) + case null => null + }).asInstanceOf[ArrayOps[T]] + + implicit def booleanArrayOps(xs: Array[Boolean]): ArrayOps[Boolean] = new ArrayOps.ofBoolean(xs) + implicit def byteArrayOps(xs: Array[Byte]): ArrayOps[Byte] = new ArrayOps.ofByte(xs) + implicit def charArrayOps(xs: Array[Char]): ArrayOps[Char] = new ArrayOps.ofChar(xs) + implicit def doubleArrayOps(xs: Array[Double]): ArrayOps[Double] = new ArrayOps.ofDouble(xs) + implicit def floatArrayOps(xs: Array[Float]): ArrayOps[Float] = new ArrayOps.ofFloat(xs) + implicit def intArrayOps(xs: Array[Int]): ArrayOps[Int] = new ArrayOps.ofInt(xs) + implicit def longArrayOps(xs: Array[Long]): ArrayOps[Long] = new ArrayOps.ofLong(xs) + implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T] = new ArrayOps.ofRef[T](xs) + implicit def shortArrayOps(xs: Array[Short]): ArrayOps[Short] = new ArrayOps.ofShort(xs) + implicit def unitArrayOps(xs: Array[Unit]): ArrayOps[Unit] = new ArrayOps.ofUnit(xs) + + // "Autoboxing" and "Autounboxing" --------------------------------------------------- + + implicit def byte2Byte(x: Byte): java.lang.Byte = java.lang.Byte.valueOf(x) + implicit def short2Short(x: Short): java.lang.Short = java.lang.Short.valueOf(x) + implicit def char2Character(x: Char): java.lang.Character = java.lang.Character.valueOf(x) + implicit def int2Integer(x: Int): java.lang.Integer = java.lang.Integer.valueOf(x) + implicit def long2Long(x: Long): java.lang.Long = java.lang.Long.valueOf(x) + implicit def float2Float(x: Float): java.lang.Float = java.lang.Float.valueOf(x) + implicit def double2Double(x: Double): java.lang.Double = java.lang.Double.valueOf(x) + implicit def boolean2Boolean(x: Boolean): java.lang.Boolean = java.lang.Boolean.valueOf(x) + + implicit def Byte2byte(x: java.lang.Byte): Byte = x.byteValue + implicit def Short2short(x: java.lang.Short): Short = x.shortValue + implicit def Character2char(x: java.lang.Character): Char = x.charValue + implicit def Integer2int(x: java.lang.Integer): Int = x.intValue + implicit def Long2long(x: java.lang.Long): Long = x.longValue + implicit def Float2float(x: java.lang.Float): Float = x.floatValue + implicit def Double2double(x: java.lang.Double): Double = x.doubleValue + implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue + + // Type Constraints -------------------------------------------------------------- + + /** + * An instance of `A <:< B` witnesses that `A` is a subtype of `B`. + * Requiring an implicit argument of the type `A <:< B` encodes + * the generalized constraint `A <: B`. + * + * @note we need a new type constructor `<:<` and evidence `conforms`, + * as reusing `Function1` and `identity` leads to ambiguities in + * case of type errors (`any2stringadd` is inferred) + * + * To constrain any abstract type T that's in scope in a method's + * argument list (not just the method's own type parameters) simply + * add an implicit argument of type `T <:< U`, where `U` is the required + * upper bound; or for lower-bounds, use: `L <:< T`, where `L` is the + * required lower bound. + * + * In part contributed by Jason Zaugg. + */ + @implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.") + sealed abstract class <:<[-From, +To] extends (From => To) with Serializable + private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x } + // The dollar prefix is to dodge accidental shadowing of this method + // by a user-defined method of the same name (SI-7788). + // The collections rely on this method. + implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A] + + @deprecated("Use `implicitly[T <:< U]` or `identity` instead.", "2.11.0") + def conforms[A]: A <:< A = $conforms[A] + + /** An instance of `A =:= B` witnesses that the types `A` and `B` are equal. + * + * @see `<:<` for expressing subtyping constraints + */ + @implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.") + sealed abstract class =:=[From, To] extends (From => To) with Serializable + private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x } + object =:= { + implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A] + } + + /** A type for which there is always an implicit value. + * @see [[scala.Array$]], method `fallbackCanBuildFrom` + */ + class DummyImplicit + + object DummyImplicit { + + /** An implicit value yielding a `DummyImplicit`. + * @see [[scala.Array$]], method `fallbackCanBuildFrom` + */ + implicit def dummyImplicit: DummyImplicit = new DummyImplicit + } +} + +private[scala] trait DeprecatedPredef { + self: Predef.type => + + // Deprecated stubs for any who may have been calling these methods directly. + @deprecated("Use `ArrowAssoc`", "2.11.0") def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x) + @deprecated("Use `Ensuring`", "2.11.0") def any2Ensuring[A](x: A): Ensuring[A] = new Ensuring(x) + @deprecated("Use `StringFormat`", "2.11.0") def any2stringfmt(x: Any): StringFormat[Any] = new StringFormat(x) + @deprecated("Use `Throwable` directly", "2.11.0") def exceptionWrapper(exc: Throwable) = new RichException(exc) + @deprecated("Use `SeqCharSequence`", "2.11.0") def seqToCharSequence(xs: scala.collection.IndexedSeq[Char]): CharSequence = new SeqCharSequence(xs) + @deprecated("Use `ArrayCharSequence`", "2.11.0") def arrayToCharSequence(xs: Array[Char]): CharSequence = new ArrayCharSequence(xs) + + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readLine(): String = StdIn.readLine() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readLine(text: String, args: Any*) = StdIn.readLine(text, args: _*) + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readBoolean() = StdIn.readBoolean() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readByte() = StdIn.readByte() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readShort() = StdIn.readShort() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readChar() = StdIn.readChar() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readInt() = StdIn.readInt() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readLong() = StdIn.readLong() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readFloat() = StdIn.readFloat() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readDouble() = StdIn.readDouble() + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readf(format: String) = StdIn.readf(format) + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readf1(format: String) = StdIn.readf1(format) + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readf2(format: String) = StdIn.readf2(format) + @deprecated("Use the method in `scala.io.StdIn`", "2.11.0") def readf3(format: String) = StdIn.readf3(format) +} + +/** The `LowPriorityImplicits` class provides implicit values that +* are valid in all Scala compilation units without explicit qualification, +* but that are partially overridden by higher-priority conversions in object +* `Predef`. +* +* @author Martin Odersky +* @since 2.8 +*/ +// SI-7335 Parents of Predef are defined in the same compilation unit to avoid +// cyclic reference errors compiling the standard library *without* a previously +// compiled copy on the classpath. +private[scala] abstract class LowPriorityImplicits { + import mutable.WrappedArray + import immutable.WrappedString + + /** We prefer the java.lang.* boxed types to these wrappers in + * any potential conflicts. Conflicts do exist because the wrappers + * need to implement ScalaNumber in order to have a symmetric equals + * method, but that implies implementing java.lang.Number as well. + * + * Note - these are inlined because they are value classes, but + * the call to xxxWrapper is not eliminated even though it does nothing. + * Even inlined, every call site does a no-op retrieval of Predef's MODULE$ + * because maybe loading Predef has side effects! + */ + @inline implicit def byteWrapper(x: Byte): runtime.RichByte = new runtime.RichByte(x) + @inline implicit def shortWrapper(x: Short): runtime.RichShort = new runtime.RichShort(x) + @inline implicit def intWrapper(x: Int): runtime.RichInt = new runtime.RichInt(x) + @inline implicit def charWrapper(c: Char): runtime.RichChar = new runtime.RichChar(c) + @inline implicit def longWrapper(x: Long): runtime.RichLong = new runtime.RichLong(x) + @inline implicit def floatWrapper(x: Float): runtime.RichFloat = new runtime.RichFloat(x) + @inline implicit def doubleWrapper(x: Double): runtime.RichDouble = new runtime.RichDouble(x) + @inline implicit def booleanWrapper(x: Boolean): runtime.RichBoolean = new runtime.RichBoolean(x) + + implicit def genericWrapArray[T](xs: Array[T]): WrappedArray[T] = + if (xs eq null) null + else WrappedArray.make(xs) + + // Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef] + // is as good as another for all T <: AnyRef. Instead of creating 100,000,000 + // unique ones by way of this implicit, let's share one. + implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): WrappedArray[T] = { + if (xs eq null) null + else if (xs.length == 0) WrappedArray.empty[T] + else new WrappedArray.ofRef[T](xs) + } + + implicit def wrapIntArray(xs: Array[Int]): WrappedArray[Int] = if (xs ne null) new WrappedArray.ofInt(xs) else null + implicit def wrapDoubleArray(xs: Array[Double]): WrappedArray[Double] = if (xs ne null) new WrappedArray.ofDouble(xs) else null + implicit def wrapLongArray(xs: Array[Long]): WrappedArray[Long] = if (xs ne null) new WrappedArray.ofLong(xs) else null + implicit def wrapFloatArray(xs: Array[Float]): WrappedArray[Float] = if (xs ne null) new WrappedArray.ofFloat(xs) else null + implicit def wrapCharArray(xs: Array[Char]): WrappedArray[Char] = if (xs ne null) new WrappedArray.ofChar(xs) else null + implicit def wrapByteArray(xs: Array[Byte]): WrappedArray[Byte] = if (xs ne null) new WrappedArray.ofByte(xs) else null + implicit def wrapShortArray(xs: Array[Short]): WrappedArray[Short] = if (xs ne null) new WrappedArray.ofShort(xs) else null + implicit def wrapBooleanArray(xs: Array[Boolean]): WrappedArray[Boolean] = if (xs ne null) new WrappedArray.ofBoolean(xs) else null + implicit def wrapUnitArray(xs: Array[Unit]): WrappedArray[Unit] = if (xs ne null) new WrappedArray.ofUnit(xs) else null + + implicit def wrapString(s: String): WrappedString = if (s ne null) new WrappedString(s) else null + implicit def unwrapString(ws: WrappedString): String = if (ws ne null) ws.self else null + + implicit def fallbackStringCanBuildFrom[T]: CanBuildFrom[String, T, immutable.IndexedSeq[T]] = + new CanBuildFrom[String, T, immutable.IndexedSeq[T]] { + def apply(from: String) = immutable.IndexedSeq.newBuilder[T] + def apply() = immutable.IndexedSeq.newBuilder[T] + } +} diff --git a/tests/link/dce-failing/link-predef-toList/Test.scala b/tests/link/dce-failing/link-predef-toList/Test.scala new file mode 100644 index 000000000000..230e370619da --- /dev/null +++ b/tests/link/dce-failing/link-predef-toList/Test.scala @@ -0,0 +1,8 @@ + +object Test { + def main(args: Array[String]): Unit = { + println("start") + args.toList + println("end") + } +} diff --git a/tests/link/dce-failing/link-zipWithIndex-1.check b/tests/link/dce-failing/link-zipWithIndex-1.check new file mode 100644 index 000000000000..10c09baa652b --- /dev/null +++ b/tests/link/dce-failing/link-zipWithIndex-1.check @@ -0,0 +1,3 @@ +a 0 +b 1 +c 2 diff --git a/tests/link/dce-failing/link-zipWithIndex-1.scala b/tests/link/dce-failing/link-zipWithIndex-1.scala new file mode 100644 index 000000000000..bf9f095e3fbd --- /dev/null +++ b/tests/link/dce-failing/link-zipWithIndex-1.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 31, classesWithReachableMethods = 7, reachableMethods = 55) + def main(args: Array[String]): Unit = { + val list = List('a', 'b', 'c') + for ((elem, idx) <- list.zipWithIndex) { + System.out.println(elem + " " + idx) + } + } + +} diff --git a/tests/link/dce-failing/link-zipWithIndex-2.check b/tests/link/dce-failing/link-zipWithIndex-2.check new file mode 100644 index 000000000000..10c09baa652b --- /dev/null +++ b/tests/link/dce-failing/link-zipWithIndex-2.check @@ -0,0 +1,3 @@ +a 0 +b 1 +c 2 diff --git a/tests/link/dce-failing/link-zipWithIndex-2.scala b/tests/link/dce-failing/link-zipWithIndex-2.scala new file mode 100644 index 000000000000..8ce14474406e --- /dev/null +++ b/tests/link/dce-failing/link-zipWithIndex-2.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 31, classesWithReachableMethods = 7, reachableMethods = 55) + def main(args: Array[String]): Unit = { + val list = List('a', 'b', 'c') + list.zipWithIndex.map { case (pname, idx) => + System.out.println(pname + " " + idx) + } + } + +} diff --git a/tests/link/dce/link-cast-1.check b/tests/link/dce/link-cast-1.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/dce/link-cast-1.scala b/tests/link/dce/link-cast-1.scala new file mode 100644 index 000000000000..bc0ba3237c6e --- /dev/null +++ b/tests/link/dce/link-cast-1.scala @@ -0,0 +1,28 @@ +import scala.annotation.internal + +object Test { + // @internal.link.CallGraphBounds(reachableClasses = 31, classesWithReachableMethods = 7, reachableMethods = 55) + def main(args: Array[String]): Unit = { + val box = new Box[Foo](null) + box.x = new Foo + val box2 = box.asInstanceOf[Box[Bar]] + box2.x = new Bar + f(box.asInstanceOf[Box[Bla]]) + f(box2.asInstanceOf[Box[Bla]]) + } + def f(b: Box[Bla]) = b.x.bla +} + +class Box[T](var x: T) + +class Foo extends Bla { + override def bla: String = getClass.toString +} + +class Bar extends Bla { + override def bla: String = getClass.toString +} + +trait Bla { + def bla: String = ??? +} diff --git a/tests/link/dce/link-classOf.check b/tests/link/dce/link-classOf.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/dce/link-classOf.scala b/tests/link/dce/link-classOf.scala new file mode 100644 index 000000000000..e4d4c51cf0b0 --- /dev/null +++ b/tests/link/dce/link-classOf.scala @@ -0,0 +1,11 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 31, classesWithReachableMethods = 7, reachableMethods = 55) + def main(args: Array[String]): Unit = { + classOf[Foo] + } +} + +class Foo +class Bar diff --git a/tests/link/dce/link-code-from-java-1.check b/tests/link/dce/link-code-from-java-1.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce/link-code-from-java-1.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce/link-code-from-java-10.check b/tests/link/dce/link-code-from-java-10.check new file mode 100644 index 000000000000..7af67a01e447 --- /dev/null +++ b/tests/link/dce/link-code-from-java-10.check @@ -0,0 +1 @@ +Foo.toString diff --git a/tests/link/dce/link-code-from-java-10.scala b/tests/link/dce/link-code-from-java-10.scala new file mode 100644 index 000000000000..7dcfd9cb06bb --- /dev/null +++ b/tests/link/dce/link-code-from-java-10.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 35, classesWithReachableMethods = 9, reachableMethods = 59) + def main(args: Array[String]): Unit = { + println(new Foo) // In this test case the standard lib is compiled separately. + } + +} + +class Foo { + @internal.link.AssertReachable + override def toString: String = "Foo.toString" +} diff --git a/tests/link/dce/link-code-from-java-11.check b/tests/link/dce/link-code-from-java-11.check new file mode 100644 index 000000000000..7af67a01e447 --- /dev/null +++ b/tests/link/dce/link-code-from-java-11.check @@ -0,0 +1 @@ +Foo.toString diff --git a/tests/link/dce/link-code-from-java-11.scala b/tests/link/dce/link-code-from-java-11.scala new file mode 100644 index 000000000000..c22cd4bad86f --- /dev/null +++ b/tests/link/dce/link-code-from-java-11.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 20, classesWithReachableMethods = 7, reachableMethods = 8) + def main(args: Array[String]): Unit = { + System.out.println((new Foo): Object) + } + +} + +class Foo { + @internal.link.AssertReachable + override def toString: String = "Foo.toString" +} diff --git a/tests/link/dce/link-code-from-java-2-a.check b/tests/link/dce/link-code-from-java-2-a.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce/link-code-from-java-2-a.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce/link-code-from-java-2-a.scala b/tests/link/dce/link-code-from-java-2-a.scala new file mode 100644 index 000000000000..9d6fac9ec394 --- /dev/null +++ b/tests/link/dce/link-code-from-java-2-a.scala @@ -0,0 +1,53 @@ +import java.util.Observable + +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 101, classesWithReachableMethods = 14, reachableMethods = 67) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + case _: java.lang.NoSuchMethodError => // agressive DCE + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val a = new A + Test.shouldDCE(a.f1) + a.hashCode() + a.equals(a) + a.toString + a.update(new Observable, null) + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new A) + } +} + +class A extends java.util.Observer { + def f1 = 42 + override def hashCode(): Int = super.hashCode() + override def equals(obj: scala.Any): Boolean = super.equals(obj) + override def toString: String = super.toString + override def update(o: Observable, arg: scala.Any): Unit = () +} diff --git a/tests/link/dce/link-code-from-java-2-b.check b/tests/link/dce/link-code-from-java-2-b.check new file mode 100644 index 000000000000..3b8b09974056 --- /dev/null +++ b/tests/link/dce/link-code-from-java-2-b.check @@ -0,0 +1 @@ +dceTest diff --git a/tests/link/dce/link-code-from-java-2-b.scala b/tests/link/dce/link-code-from-java-2-b.scala new file mode 100644 index 000000000000..6d34c1e7575f --- /dev/null +++ b/tests/link/dce/link-code-from-java-2-b.scala @@ -0,0 +1,53 @@ +import java.util.Observable + +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 102, classesWithReachableMethods = 18, reachableMethods = 68) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + case _: java.lang.NoSuchMethodError => // agressive DCE + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val b = new B {} + Test.shouldDCE(b.f1) + b.hashCode() + b.equals(b) + b.toString + b.update(new Observable, null) + } + + @scala.EntryPoint def entryPoint(): Unit = { + System.out.print(new B {}) + } +} + +abstract class B extends java.util.Observer { + def f1 = 42 + override def hashCode(): Int = super.hashCode() + override def equals(obj: scala.Any): Boolean = super.equals(obj) + override def toString: String = super.toString + override def update(o: Observable, arg: scala.Any): Unit = () +} diff --git a/tests/link/dce/link-code-from-java-6.check b/tests/link/dce/link-code-from-java-6.check new file mode 100644 index 000000000000..9364ec4f3096 --- /dev/null +++ b/tests/link/dce/link-code-from-java-6.check @@ -0,0 +1,3 @@ +1 +3 +4 diff --git a/tests/link/dce/link-code-from-java-6.scala b/tests/link/dce/link-code-from-java-6.scala new file mode 100644 index 000000000000..a52fdf509376 --- /dev/null +++ b/tests/link/dce/link-code-from-java-6.scala @@ -0,0 +1,20 @@ +import scala.annotation.internal + +class MyInt(val x: Int) extends java.lang.Comparable[MyInt] { + override def compareTo(that: MyInt) = this.x - that.x + override def toString = x.toString +} + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 27, classesWithReachableMethods = 10, reachableMethods = 15) + def main(args: Array[String]): Unit = { + val a = new Array[java.lang.Object](3) + a(0) = new MyInt(1) + a(1) = new MyInt(4) + a(2) = new MyInt(3) + java.util.Arrays.sort(a) + System.out.println(a(0)) + System.out.println(a(1)) + System.out.println(a(2)) + } +} diff --git a/tests/link/dce/link-code-from-java-7.check b/tests/link/dce/link-code-from-java-7.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-code-from-java-7.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-code-from-java-7.scala b/tests/link/dce/link-code-from-java-7.scala new file mode 100644 index 000000000000..a5d9c3fc0899 --- /dev/null +++ b/tests/link/dce/link-code-from-java-7.scala @@ -0,0 +1,20 @@ +import scala.annotation.internal + +import java.lang.InheritableThreadLocal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 27, classesWithReachableMethods = 10, reachableMethods = 14) + def main(args: Array[String]): Unit = { + val a = new DynamicVariable2(42) + System.out.println(a.value) + } +} + + +class DynamicVariable2[T](init: T) { + private val tl = new InheritableThreadLocal[T] { + override def initialValue = init.asInstanceOf[T with AnyRef] + } + + def value: T = tl.get.asInstanceOf[T] +} diff --git a/tests/link/dce/link-code-from-java-8.check b/tests/link/dce/link-code-from-java-8.check new file mode 100644 index 000000000000..7af67a01e447 --- /dev/null +++ b/tests/link/dce/link-code-from-java-8.check @@ -0,0 +1 @@ +Foo.toString diff --git a/tests/link/dce/link-code-from-java-8.scala b/tests/link/dce/link-code-from-java-8.scala new file mode 100644 index 000000000000..299fa85ac8fd --- /dev/null +++ b/tests/link/dce/link-code-from-java-8.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 6, reachableMethods = 8) + def main(args: Array[String]): Unit = { + System.out.println(new Foo) + } + +} + +class Foo { + @internal.link.AssertReachable + override def toString: String = "Foo.toString" +} diff --git a/tests/link/dce/link-code-from-java-9.check b/tests/link/dce/link-code-from-java-9.check new file mode 100644 index 000000000000..7af67a01e447 --- /dev/null +++ b/tests/link/dce/link-code-from-java-9.check @@ -0,0 +1 @@ +Foo.toString diff --git a/tests/link/dce/link-code-from-java-9.scala b/tests/link/dce/link-code-from-java-9.scala new file mode 100644 index 000000000000..8e7eee465461 --- /dev/null +++ b/tests/link/dce/link-code-from-java-9.scala @@ -0,0 +1,18 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 7, reachableMethods = 9) + def main(args: Array[String]): Unit = { + foo(new Foo) + } + + def foo(s: Object) = { + System.out.println(s) + } + +} + +class Foo { + @internal.link.AssertReachable + override def toString: String = "Foo.toString" +} diff --git a/tests/link/dce/link-constructorParams.check b/tests/link/dce/link-constructorParams.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-constructorParams.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-constructorParams.scala b/tests/link/dce/link-constructorParams.scala new file mode 100644 index 000000000000..eaea6cff384e --- /dev/null +++ b/tests/link/dce/link-constructorParams.scala @@ -0,0 +1,11 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 23, classesWithReachableMethods = 8, reachableMethods = 10) + def main(args: Array[String]): Unit = { + System.out.println(new Foo(42).x) + } +} + +class Foo(x: Int) extends Bar(x) +class Bar(@internal.link.AssertReachable val x: Int) diff --git a/tests/link/dce/link-entry-point-1.check b/tests/link/dce/link-entry-point-1.check new file mode 100644 index 000000000000..b0c9defbb9fb --- /dev/null +++ b/tests/link/dce/link-entry-point-1.check @@ -0,0 +1,2 @@ +dceTest +42 diff --git a/tests/link/dce/link-entry-point-1.scala b/tests/link/dce/link-entry-point-1.scala new file mode 100644 index 000000000000..856222f2f7e0 --- /dev/null +++ b/tests/link/dce/link-entry-point-1.scala @@ -0,0 +1,41 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 99, classesWithReachableMethods = 14, reachableMethods = 62) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + case _: java.lang.NoSuchMethodError => // agressive DCE + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + + val x = new Foo + Test.shouldDCE(x.bar()) + x.foo() + } +} + +class Foo { + @scala.EntryPoint def foo(): Unit = System.out.println(42) + @internal.link.AssertNotReachable def bar(): Unit = System.out.println(43) +} diff --git a/tests/link/dce/link-entry-point-2.check b/tests/link/dce/link-entry-point-2.check new file mode 100644 index 000000000000..b0c9defbb9fb --- /dev/null +++ b/tests/link/dce/link-entry-point-2.check @@ -0,0 +1,2 @@ +dceTest +42 diff --git a/tests/link/dce/link-entry-point-2.scala b/tests/link/dce/link-entry-point-2.scala new file mode 100644 index 000000000000..ea5ab6dcd52b --- /dev/null +++ b/tests/link/dce/link-entry-point-2.scala @@ -0,0 +1,39 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 99, classesWithReachableMethods = 14, reachableMethods = 63) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + case _: java.lang.NoSuchMethodError => // agressive DCE + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + Test.shouldDCE(Foo.bar()) + Foo.foo() + } +} + +object Foo { + @scala.EntryPoint def foo(): Unit = System.out.println(42) + @internal.link.AssertNotReachable def bar(): Unit = System.out.println(43) +} diff --git a/tests/link/dce/link-entry-point-3-a.check b/tests/link/dce/link-entry-point-3-a.check new file mode 100644 index 000000000000..677051eb2f1a --- /dev/null +++ b/tests/link/dce/link-entry-point-3-a.check @@ -0,0 +1,3 @@ +43 +43 +42 diff --git a/tests/link/dce/link-entry-point-3-a.scala b/tests/link/dce/link-entry-point-3-a.scala new file mode 100644 index 000000000000..7c969713c169 --- /dev/null +++ b/tests/link/dce/link-entry-point-3-a.scala @@ -0,0 +1,31 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 99, classesWithReachableMethods = 14, reachableMethods = 64) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + Foo.bar() + Foo.foo() + } +} + +object Foo { + + bar() + + @scala.EntryPoint def foo(): Unit = System.out.println(42) + @internal.link.AssertReachable def bar(): Unit = System.out.println(43) +} diff --git a/tests/link/dce/link-entry-point-3-b.check b/tests/link/dce/link-entry-point-3-b.check new file mode 100644 index 000000000000..677051eb2f1a --- /dev/null +++ b/tests/link/dce/link-entry-point-3-b.check @@ -0,0 +1,3 @@ +43 +43 +42 diff --git a/tests/link/dce/link-entry-point-3-b.scala b/tests/link/dce/link-entry-point-3-b.scala new file mode 100644 index 000000000000..b8c0393b0f6f --- /dev/null +++ b/tests/link/dce/link-entry-point-3-b.scala @@ -0,0 +1,36 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 100, classesWithReachableMethods = 19, reachableMethods = 66) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + foo.Foo.bar() + foo.Foo.foo() + } +} + +package foo { + + object Foo { + + bar() + + @scala.EntryPoint def foo(): Unit = System.out.println(42) + + @internal.link.AssertReachable def bar(): Unit = System.out.println(43) + } + +} \ No newline at end of file diff --git a/tests/link/dce/link-entry-point-4.check b/tests/link/dce/link-entry-point-4.check new file mode 100644 index 000000000000..885a51e592eb --- /dev/null +++ b/tests/link/dce/link-entry-point-4.check @@ -0,0 +1,4 @@ +dceTest +43 +43 +42 diff --git a/tests/link/dce/link-entry-point-4.scala b/tests/link/dce/link-entry-point-4.scala new file mode 100644 index 000000000000..a17434956093 --- /dev/null +++ b/tests/link/dce/link-entry-point-4.scala @@ -0,0 +1,42 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 99, classesWithReachableMethods = 14, reachableMethods = 65) + def main(args: Array[String]): Unit = { + val classLoader = Test.getClass.getClassLoader() + + try { + val mainClass = classLoader.loadClass("Test") + val mainMethod = mainClass.getMethod("dceTest") + mainMethod.invoke(null); + } catch { + case e: java.lang.Exception => e.getCause.printStackTrace() + } + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def shouldDCE(expr: => Any): Unit = try { + expr + throw new Exception("Expected DCE") + } catch { + case dce: dotty.runtime.DeadCodeEliminated => + } + + @internal.link.AssertNotReachable + @internal.link.DoNotDeadCodeEliminate + def dceTest: Unit = { + System.out.println("dceTest") + Foo.bar() + Foo.foo() + } + + object Foo { + + bar() + + @scala.EntryPoint def foo(): Unit = System.out.println(42) + @internal.link.AssertReachable def bar(): Unit = System.out.println(43) + } +} + diff --git a/tests/link/dce/link-field-lambda.check b/tests/link/dce/link-field-lambda.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-field-lambda.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-field-lambda.scala b/tests/link/dce/link-field-lambda.scala new file mode 100644 index 000000000000..08f3edb3093a --- /dev/null +++ b/tests/link/dce/link-field-lambda.scala @@ -0,0 +1,13 @@ +import scala.annotation.internal + +object Test { + val f = (x: Int) => 2 * x + val g = (x: Int) => 3 * x + + def lala(x: Boolean): Int=>Int = if (x) f else g + + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 7, reachableMethods = 11) + def main(args: Array[String]): Unit = { + System.out.println(lala(true)(21)) + } +} \ No newline at end of file diff --git a/tests/link/dce/link-fillStackTrace-0.check b/tests/link/dce/link-fillStackTrace-0.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-fillStackTrace-0.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-fillStackTrace-0.scala b/tests/link/dce/link-fillStackTrace-0.scala new file mode 100644 index 000000000000..0ac3b6e75b8b --- /dev/null +++ b/tests/link/dce/link-fillStackTrace-0.scala @@ -0,0 +1,17 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 23, classesWithReachableMethods = 7, reachableMethods = 9) + def main(args: Array[String]): Unit = { + try { + throw new ThrowableFoo + } catch { + case _: ThrowableFoo => System.out.println(42) + } + } +} + +class ThrowableFoo extends Throwable { + @internal.link.AssertReachable + override def fillInStackTrace(): Throwable = this +} diff --git a/tests/link/dce/link-fillStackTrace-1.check b/tests/link/dce/link-fillStackTrace-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-fillStackTrace-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-fillStackTrace-1.scala b/tests/link/dce/link-fillStackTrace-1.scala new file mode 100644 index 000000000000..49c929d3a79c --- /dev/null +++ b/tests/link/dce/link-fillStackTrace-1.scala @@ -0,0 +1,18 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 24, classesWithReachableMethods = 9, reachableMethods = 9) + def main(args: Array[String]): Unit = { + try { + throw new BreakControl + } catch { + case _: BreakControl => System.out.println(42) + } + } +} + +class BreakControl extends NoStackTrace + +trait NoStackTrace extends Throwable { + override def fillInStackTrace(): Throwable = this +} diff --git a/tests/link/dce/link-implicit-constructor.check b/tests/link/dce/link-implicit-constructor.check new file mode 100644 index 000000000000..b6fc4c620b67 --- /dev/null +++ b/tests/link/dce/link-implicit-constructor.check @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/tests/link/dce/link-implicit-constructor.scala b/tests/link/dce/link-implicit-constructor.scala new file mode 100644 index 000000000000..375fc5ef7bee --- /dev/null +++ b/tests/link/dce/link-implicit-constructor.scala @@ -0,0 +1,13 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 35, classesWithReachableMethods = 10, reachableMethods = 60) + def main(args: Array[String]): Unit = { + implicit def s: String = "hello" + new Foo + } +} + +class Foo(implicit str: String) { + println(str) +} diff --git a/tests/link/dce/link-initial-values.check b/tests/link/dce/link-initial-values.check new file mode 100644 index 000000000000..920a13966480 --- /dev/null +++ b/tests/link/dce/link-initial-values.check @@ -0,0 +1 @@ +43 diff --git a/tests/link/dce/link-initial-values.scala b/tests/link/dce/link-initial-values.scala new file mode 100644 index 000000000000..238263fddfaf --- /dev/null +++ b/tests/link/dce/link-initial-values.scala @@ -0,0 +1,18 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 8, reachableMethods = 9) + def main(args: Array[String]): Unit = { + new Bar + } +} + + +class Bar extends Foo + +trait Foo { + val foo: Int = { + System.out.println(43) + 43 + } +} diff --git a/tests/link/dce/link-inner-class-call-outer-0.check b/tests/link/dce/link-inner-class-call-outer-0.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-0.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-inner-class-call-outer-0.scala b/tests/link/dce/link-inner-class-call-outer-0.scala new file mode 100644 index 000000000000..eb057e9e8434 --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-0.scala @@ -0,0 +1,25 @@ + + +object Test { + def main(args: Array[String]): Unit = { + System.out.println(new OfInt().iterator.next()) + } +} + +class OfInt extends IndexedSeqLike2[Int] { + def foo(index: Int): Int = 42 +} + +trait IndexedSeqLike2[A] { + + def foo(index: Int): A + def iterator: Elements = new Elements + + class Elements extends Iterator2[A] { + def next(): A = foo(0) + } +} + +trait Iterator2[A] { + def next(): A +} diff --git a/tests/link/dce/link-inner-class-call-outer-1.check b/tests/link/dce/link-inner-class-call-outer-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-inner-class-call-outer-1.scala b/tests/link/dce/link-inner-class-call-outer-1.scala new file mode 100644 index 000000000000..0e8375dc1af8 --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-1.scala @@ -0,0 +1,25 @@ + + +object Test { + def main(args: Array[String]): Unit = { + System.out.println(new OfInt().iterator.next()) + } +} + +class OfInt extends IndexedSeqLike2[Int] { + def foo(index: Int): Int = 42 +} + +trait IndexedSeqLike2[A] { + + def foo(index: Int): A + def iterator: Elements[A] = new Elements[A] + + class Elements[B >: A] extends Iterator2[B] { + def next(): B = foo(0) + } +} + +trait Iterator2[A] { + def next(): A +} diff --git a/tests/link/dce/link-inner-class-call-outer-2.check b/tests/link/dce/link-inner-class-call-outer-2.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-2.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-inner-class-call-outer-2.scala b/tests/link/dce/link-inner-class-call-outer-2.scala new file mode 100644 index 000000000000..8e678aab129b --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-2.scala @@ -0,0 +1,25 @@ + + +object Test { + def main(args: Array[String]): Unit = { + System.out.println(new OfInt().iterator.next()) + } +} + +class OfInt extends IndexedSeqLike2 { + def foo(index: Int): Int = 42 +} + +trait IndexedSeqLike2 { + + def foo(index: Int): Int + def iterator: Elements = new Elements + + class Elements extends Iterator2[Int] { + def next(): Int = foo(0) + } +} + +trait Iterator2[A] { + def next(): Int +} diff --git a/tests/link/dce/link-inner-class-call-outer-3.check b/tests/link/dce/link-inner-class-call-outer-3.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-3.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-inner-class-call-outer-3.scala b/tests/link/dce/link-inner-class-call-outer-3.scala new file mode 100644 index 000000000000..a0f7c7911eed --- /dev/null +++ b/tests/link/dce/link-inner-class-call-outer-3.scala @@ -0,0 +1,25 @@ + + +object Test { + def main(args: Array[String]): Unit = { + System.out.println(new OfInt().iterator.next()) + } +} + +class OfInt extends IndexedSeqLike2 { + def foo(index: Int): Int = 42 +} + +trait IndexedSeqLike2 { + + def foo(index: Int): Int + def iterator: Elements = new Elements + + class Elements extends Iterator2 { + def next(): Int = foo(0) + } +} + +trait Iterator2 { + def next(): Int +} diff --git a/tests/link/dce/link-inner-object.check b/tests/link/dce/link-inner-object.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-inner-object.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-inner-object.scala b/tests/link/dce/link-inner-object.scala new file mode 100644 index 000000000000..30d141ef656c --- /dev/null +++ b/tests/link/dce/link-inner-object.scala @@ -0,0 +1,11 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 19, classesWithReachableMethods = 8, reachableMethods = 9) + def main(args: Array[String]): Unit = { + object Foo { + def test() = 42 + } + System.out.println(Foo.test()) + } +} diff --git a/tests/link/dce/link-innerClass-0.check b/tests/link/dce/link-innerClass-0.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-innerClass-0.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-innerClass-0.scala b/tests/link/dce/link-innerClass-0.scala new file mode 100644 index 000000000000..e4d1cb897ee8 --- /dev/null +++ b/tests/link/dce/link-innerClass-0.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 19, classesWithReachableMethods = 7, reachableMethods = 8) + def main(args: Array[String]): Unit = { + class Foo { + @internal.link.AssertReachable + def bar: Int = 42 + } + + val foo = new Foo + System.out.println(foo.bar) + } +} diff --git a/tests/link/dce/link-innerClass-1.check b/tests/link/dce/link-innerClass-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-innerClass-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-innerClass-1.scala b/tests/link/dce/link-innerClass-1.scala new file mode 100644 index 000000000000..b4e3f8a7dc44 --- /dev/null +++ b/tests/link/dce/link-innerClass-1.scala @@ -0,0 +1,20 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 8, reachableMethods = 10) + def main(args: Array[String]): Unit = { + new Bar().test() + } +} + +class Bar { + def test() = { + class Foo { + @internal.link.AssertReachable + def bar: Int = 42 + } + + val foo = new Foo + System.out.println(foo.bar) + } +} diff --git a/tests/link/dce/link-innerFunction-0.check b/tests/link/dce/link-innerFunction-0.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-innerFunction-0.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-innerFunction-0.scala b/tests/link/dce/link-innerFunction-0.scala new file mode 100644 index 000000000000..453efed98237 --- /dev/null +++ b/tests/link/dce/link-innerFunction-0.scala @@ -0,0 +1,24 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 9, reachableMethods = 11) + def main(args: Array[String]): Unit = { + val bar = new Bar + bar.foo1(42) + } +} + +class Bar extends Baz[Int] + +class Baz[A] { + + @internal.link.AssertReachable def foo1(x: A): Unit = { + @internal.link.AssertReachable def innerFoo(x: A): Unit = foo2(x) + innerFoo(x) + } + + @internal.link.AssertReachable def foo2(elem: A): Unit = { + System.out.println(elem.toString) + } + +} diff --git a/tests/link/dce/link-interface-no-init.check b/tests/link/dce/link-interface-no-init.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-interface-no-init.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-interface-no-init/Foo.java b/tests/link/dce/link-interface-no-init/Foo.java new file mode 100644 index 000000000000..679a958225e4 --- /dev/null +++ b/tests/link/dce/link-interface-no-init/Foo.java @@ -0,0 +1,4 @@ + +interface Foo { + +} diff --git a/tests/link/dce/link-interface-no-init/MainGenericRunner.java b/tests/link/dce/link-interface-no-init/MainGenericRunner.java new file mode 100644 index 000000000000..ac1b4feaeb3a --- /dev/null +++ b/tests/link/dce/link-interface-no-init/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex); + } + } +} diff --git a/tests/link/dce/link-interface-no-init/Test.scala b/tests/link/dce/link-interface-no-init/Test.scala new file mode 100644 index 000000000000..21754ff01cd9 --- /dev/null +++ b/tests/link/dce/link-interface-no-init/Test.scala @@ -0,0 +1,17 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 23, classesWithReachableMethods = 8, reachableMethods = 8) + def main(args: Array[String]): Unit = { + new Baz + } + +} + +class Baz extends Foo with Bar1 with Bar2 + +trait Bar1 + +trait Bar2 { + System.out.println(42) +} \ No newline at end of file diff --git a/tests/link/dce/link-java-polimorphic-method-1.check b/tests/link/dce/link-java-polimorphic-method-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-java-polimorphic-method-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-java-polimorphic-method-1/Foo.java b/tests/link/dce/link-java-polimorphic-method-1/Foo.java new file mode 100644 index 000000000000..df96d414ffb9 --- /dev/null +++ b/tests/link/dce/link-java-polimorphic-method-1/Foo.java @@ -0,0 +1,4 @@ + +public interface Foo { + public T foo(T t); +} \ No newline at end of file diff --git a/tests/link/dce/link-java-polimorphic-method-1/Test.scala b/tests/link/dce/link-java-polimorphic-method-1/Test.scala new file mode 100644 index 000000000000..9543db0e9323 --- /dev/null +++ b/tests/link/dce/link-java-polimorphic-method-1/Test.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 7, reachableMethods = 9) + def main(args: Array[String]): Unit = { + System.out.println(new Bar) + } +} + +class Bar extends Foo { + def foo[T](t: T): T = t + + override def toString(): String = foo("42") +} \ No newline at end of file diff --git a/tests/link/dce/link-java-polimorphic-method-2.check b/tests/link/dce/link-java-polimorphic-method-2.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-java-polimorphic-method-2.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-java-polimorphic-method-2/Foo.java b/tests/link/dce/link-java-polimorphic-method-2/Foo.java new file mode 100644 index 000000000000..c729cf6e193b --- /dev/null +++ b/tests/link/dce/link-java-polimorphic-method-2/Foo.java @@ -0,0 +1,6 @@ + +public interface Foo { + public String foo(T t); + + public String string(); +} \ No newline at end of file diff --git a/tests/link/dce/link-java-polimorphic-method-2/Test.scala b/tests/link/dce/link-java-polimorphic-method-2/Test.scala new file mode 100644 index 000000000000..a0208e997e3c --- /dev/null +++ b/tests/link/dce/link-java-polimorphic-method-2/Test.scala @@ -0,0 +1,16 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 7, reachableMethods = 10) + def main(args: Array[String]): Unit = { + System.out.println(new Bar) + } +} + +class Bar extends Foo { + def foo[T <: Foo](t: T): String = t.string() + + def string(): String = "42" + + override def toString(): String = foo(this) +} \ No newline at end of file diff --git a/tests/link/dce/link-lib-1.check b/tests/link/dce/link-lib-1.check new file mode 100644 index 000000000000..b4db3ed707d8 --- /dev/null +++ b/tests/link/dce/link-lib-1.check @@ -0,0 +1,2 @@ +43 +44 diff --git a/tests/link/dce/link-lib-1/App_2.scala b/tests/link/dce/link-lib-1/App_2.scala new file mode 100644 index 000000000000..71c7faada4f9 --- /dev/null +++ b/tests/link/dce/link-lib-1/App_2.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + new Foo().foo + } +} diff --git a/tests/link/dce/link-lib-1/Lib_1.scala b/tests/link/dce/link-lib-1/Lib_1.scala new file mode 100644 index 000000000000..cfd9f1b9cfbd --- /dev/null +++ b/tests/link/dce/link-lib-1/Lib_1.scala @@ -0,0 +1,13 @@ + +class Foo { + + def foo = { + println(43) + bar + } + + def bar = println(44) + + def baz = println(45) + +} diff --git a/tests/link/dce/link-lib-2.check b/tests/link/dce/link-lib-2.check new file mode 100644 index 000000000000..b4db3ed707d8 --- /dev/null +++ b/tests/link/dce/link-lib-2.check @@ -0,0 +1,2 @@ +43 +44 diff --git a/tests/link/dce/link-lib-2/App_3.scala b/tests/link/dce/link-lib-2/App_3.scala new file mode 100644 index 000000000000..e6a1290bd4ae --- /dev/null +++ b/tests/link/dce/link-lib-2/App_3.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + new Bar().barbar + } +} diff --git a/tests/link/dce/link-lib-2/Lib_1.scala b/tests/link/dce/link-lib-2/Lib_1.scala new file mode 100644 index 000000000000..cfd9f1b9cfbd --- /dev/null +++ b/tests/link/dce/link-lib-2/Lib_1.scala @@ -0,0 +1,13 @@ + +class Foo { + + def foo = { + println(43) + bar + } + + def bar = println(44) + + def baz = println(45) + +} diff --git a/tests/link/dce/link-lib-2/Lib_2.scala b/tests/link/dce/link-lib-2/Lib_2.scala new file mode 100644 index 000000000000..04bd951db93c --- /dev/null +++ b/tests/link/dce/link-lib-2/Lib_2.scala @@ -0,0 +1,4 @@ + +class Bar extends Foo { + def barbar = foo +} diff --git a/tests/link/dce/link-local-lambda.check b/tests/link/dce/link-local-lambda.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-local-lambda.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-local-lambda.scala b/tests/link/dce/link-local-lambda.scala new file mode 100644 index 000000000000..b617a3d6a2f0 --- /dev/null +++ b/tests/link/dce/link-local-lambda.scala @@ -0,0 +1,8 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 7, reachableMethods = 7) + def main(args: Array[String]): Unit = { + System.out.println((() => 42)()) + } +} \ No newline at end of file diff --git a/tests/link/dce/link-local-lazy-val.check b/tests/link/dce/link-local-lazy-val.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-local-lazy-val.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-local-lazy-val.scala b/tests/link/dce/link-local-lazy-val.scala new file mode 100644 index 000000000000..1b78648debc9 --- /dev/null +++ b/tests/link/dce/link-local-lazy-val.scala @@ -0,0 +1,9 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 7, reachableMethods = 7) + def main(args: Array[String]): Unit = { + lazy val x = 42 + System.out.println(x) + } +} \ No newline at end of file diff --git a/tests/link/dce/link-main-module-load.check b/tests/link/dce/link-main-module-load.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-main-module-load.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-main-module-load.scala b/tests/link/dce/link-main-module-load.scala new file mode 100644 index 000000000000..3bcc492c200a --- /dev/null +++ b/tests/link/dce/link-main-module-load.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + + @internal.link.CallGraphBounds(reachableClasses = 19, classesWithReachableMethods = 6, reachableMethods = 7) + def main(args: Array[String]): Unit = () + + @internal.link.AssertReachable def foo() = System.out.println(42) + + foo() + +} diff --git a/tests/link/dce/link-mixinInit-1.check b/tests/link/dce/link-mixinInit-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-mixinInit-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-mixinInit-1.scala b/tests/link/dce/link-mixinInit-1.scala new file mode 100644 index 000000000000..1d534951985b --- /dev/null +++ b/tests/link/dce/link-mixinInit-1.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 8, reachableMethods = 8) + def main(args: Array[String]): Unit = { + new Bar + } +} + +class Bar extends Foo + +trait Foo { + System.out.println(42) +} diff --git a/tests/link/dce/link-mixinInit-2.check b/tests/link/dce/link-mixinInit-2.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-mixinInit-2.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-mixinInit-2.scala b/tests/link/dce/link-mixinInit-2.scala new file mode 100644 index 000000000000..9729407df373 --- /dev/null +++ b/tests/link/dce/link-mixinInit-2.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 8, reachableMethods = 9) + def main(args: Array[String]): Unit = { + new Bar + } +} + +class Bar extends Foo(42) + +trait Foo(n: Int) { + System.out.println(n) +} diff --git a/tests/link/dce/link-mixinInit-3.check b/tests/link/dce/link-mixinInit-3.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-mixinInit-3.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-mixinInit-3.scala b/tests/link/dce/link-mixinInit-3.scala new file mode 100644 index 000000000000..f25ac2ea98e5 --- /dev/null +++ b/tests/link/dce/link-mixinInit-3.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 8, reachableMethods = 8) + def main(args: Array[String]): Unit = { + new Bar + } +} + +class Bar extends Foo[Int] + +trait Foo[T] { + System.out.println(42) +} diff --git a/tests/link/dce/link-mixinInit-4.check b/tests/link/dce/link-mixinInit-4.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-mixinInit-4.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-mixinInit-4.scala b/tests/link/dce/link-mixinInit-4.scala new file mode 100644 index 000000000000..74216fa71cdb --- /dev/null +++ b/tests/link/dce/link-mixinInit-4.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 23, classesWithReachableMethods = 7, reachableMethods = 9) + def main(args: Array[String]): Unit = { + new Bar + } +} + +class Bar extends Foo[Int](42) + +trait Foo[T](n: Int) { + System.out.println(n) +} diff --git a/tests/link/dce/link-mixinInit-5.check b/tests/link/dce/link-mixinInit-5.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-mixinInit-5.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-mixinInit-5.scala b/tests/link/dce/link-mixinInit-5.scala new file mode 100644 index 000000000000..4b624d83780b --- /dev/null +++ b/tests/link/dce/link-mixinInit-5.scala @@ -0,0 +1,19 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 24, classesWithReachableMethods = 7, reachableMethods = 10) + def main(args: Array[String]): Unit = { + new Foo + } +} + +class Foo extends Bar { + def foo(): Unit = System.out.println(42) +} + +trait Bar extends Baz + +trait Baz { + foo() + def foo(): Unit +} diff --git a/tests/link/dce/link-mixinInit-6.check b/tests/link/dce/link-mixinInit-6.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-mixinInit-6.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-mixinInit-6.scala b/tests/link/dce/link-mixinInit-6.scala new file mode 100644 index 000000000000..33b5e877a630 --- /dev/null +++ b/tests/link/dce/link-mixinInit-6.scala @@ -0,0 +1,21 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 25, classesWithReachableMethods = 8, reachableMethods = 11) + def main(args: Array[String]): Unit = { + new Foo + } +} + +class Foo extends Foo2 with Bar { + def foo(): Unit = System.out.println(42) +} + +abstract class Foo2 extends Baz + +trait Bar extends Baz + +trait Baz { + foo() + def foo(): Unit +} diff --git a/tests/link/dce/link-predef-toList.check b/tests/link/dce/link-predef-toList.check new file mode 100644 index 000000000000..600d9f049192 --- /dev/null +++ b/tests/link/dce/link-predef-toList.check @@ -0,0 +1,3 @@ +start +Predef loaded +end diff --git a/tests/link/dce/link-secondary-constructor-0.check b/tests/link/dce/link-secondary-constructor-0.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-secondary-constructor-0.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-secondary-constructor-0.scala b/tests/link/dce/link-secondary-constructor-0.scala new file mode 100644 index 000000000000..f9dc06beb5d8 --- /dev/null +++ b/tests/link/dce/link-secondary-constructor-0.scala @@ -0,0 +1,15 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 7, reachableMethods = 10) + def main(args: Array[String]): Unit = { + new Foo() + } +} + +class Foo(n: Int) { + def this() = this(42) + + @internal.link.AssertReachable def foo() = System.out.println(n) + foo() +} diff --git a/tests/link/dce/link-super-call-1.check b/tests/link/dce/link-super-call-1.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/dce/link-super-call-1.scala b/tests/link/dce/link-super-call-1.scala new file mode 100644 index 000000000000..ea86cd22ab1d --- /dev/null +++ b/tests/link/dce/link-super-call-1.scala @@ -0,0 +1,18 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 10, classesWithReachableMethods = 4, reachableMethods = 8) + def main(args: Array[String]): Unit = { + val a = new Foo + a.foo() + } +} + +class Foo extends Bar { + @internal.link.AssertReachable def foo(): Unit = super.bar() +} + +trait Bar { + @internal.link.AssertReachable def bar(): Unit = baz() + @internal.link.AssertReachable def baz(): Unit = () +} diff --git a/tests/link/dce/link-system-println.check b/tests/link/dce/link-system-println.check new file mode 100644 index 000000000000..20b841e036ab --- /dev/null +++ b/tests/link/dce/link-system-println.check @@ -0,0 +1,3 @@ +hello +1 +true diff --git a/tests/link/dce/link-system-println.scala b/tests/link/dce/link-system-println.scala new file mode 100644 index 000000000000..31a937ab5cd1 --- /dev/null +++ b/tests/link/dce/link-system-println.scala @@ -0,0 +1,10 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 20, classesWithReachableMethods = 6, reachableMethods = 8) + def main(args: Array[String]): Unit = { + System.out.println("hello") + System.out.println(1) + System.out.println(true) + } +} diff --git a/tests/link/dce/link-trait-overrides.check b/tests/link/dce/link-trait-overrides.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-trait-overrides.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-trait-overrides.scala b/tests/link/dce/link-trait-overrides.scala new file mode 100644 index 000000000000..06788356235f --- /dev/null +++ b/tests/link/dce/link-trait-overrides.scala @@ -0,0 +1,19 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 24, classesWithReachableMethods = 7, reachableMethods = 8) + def main(args: Array[String]): Unit = { + new Foo().foo + } +} + +class Foo extends Bar + +trait Bar extends Baz { + override def foo = System.out.println(42) +} + +trait Baz { + @internal.link.AssertNotReachable + def foo = System.out.println(5) +} diff --git a/tests/link/dce/link-val-init-1.check b/tests/link/dce/link-val-init-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-val-init-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-val-init-1.scala b/tests/link/dce/link-val-init-1.scala new file mode 100644 index 000000000000..a00369cc9efb --- /dev/null +++ b/tests/link/dce/link-val-init-1.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +class Foo { + @internal.link.AssertReachable + def foo = System.out.println(42) + val bar = foo +} + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 7, reachableMethods = 9) + def main(args: Array[String]): Unit = { + new Foo + } +} diff --git a/tests/link/dce/link-val-init-2.check b/tests/link/dce/link-val-init-2.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-val-init-2.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-val-init-2.scala b/tests/link/dce/link-val-init-2.scala new file mode 100644 index 000000000000..699a4ce2349f --- /dev/null +++ b/tests/link/dce/link-val-init-2.scala @@ -0,0 +1,17 @@ +import scala.annotation.internal + +class Foo { + @internal.link.AssertReachable + def foo = System.out.println(42) + + { + val bar = foo + } +} + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 8, reachableMethods = 9) + def main(args: Array[String]): Unit = { + new Foo + } +} diff --git a/tests/link/dce/link-val-init-3.check b/tests/link/dce/link-val-init-3.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/dce/link-val-init-3.scala b/tests/link/dce/link-val-init-3.scala new file mode 100644 index 000000000000..c30e79f46e42 --- /dev/null +++ b/tests/link/dce/link-val-init-3.scala @@ -0,0 +1,14 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 10, classesWithReachableMethods = 4, reachableMethods = 6) + def main(args: Array[String]): Unit = { + new Foo + } +} + +final class Foo extends Bar[Int] + +trait Bar[T] { + val value: Int = 42 +} diff --git a/tests/link/dce/link-var-setters-1.check b/tests/link/dce/link-var-setters-1.check new file mode 100644 index 000000000000..192e9d9a1f33 --- /dev/null +++ b/tests/link/dce/link-var-setters-1.check @@ -0,0 +1,2 @@ +1 +42 diff --git a/tests/link/dce/link-var-setters-1.scala b/tests/link/dce/link-var-setters-1.scala new file mode 100644 index 000000000000..64fa304333fe --- /dev/null +++ b/tests/link/dce/link-var-setters-1.scala @@ -0,0 +1,21 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 7, reachableMethods = 9) + def main(args: Array[String]): Unit = { + val foo = new Foo + System.out.println(foo.bar) + foo.baz() + System.out.println(foo.bar) + } +} + + +class Foo { + var bar = 1 + + @internal.link.AssertReachable + def baz(): Unit = { + bar = 42 + } +} diff --git a/tests/link/dce/link-varargs-Any-empty.check b/tests/link/dce/link-varargs-Any-empty.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-Any-empty.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-Any-empty/MainGenericRunner.java b/tests/link/dce/link-varargs-Any-empty/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-Any-empty/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-Any-empty/Test.scala b/tests/link/dce/link-varargs-Any-empty/Test.scala new file mode 100644 index 000000000000..66a3cae92a3a --- /dev/null +++ b/tests/link/dce/link-varargs-Any-empty/Test.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 171, classesWithReachableMethods = 50, reachableMethods = 211) + def main(args: Array[String]): Unit = { + foo() + } + + def foo(ns: Any*): Unit = { + System.out.println(42) + } +} diff --git a/tests/link/dce/link-varargs-Any.check b/tests/link/dce/link-varargs-Any.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-Any.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-Any/MainGenericRunner.java b/tests/link/dce/link-varargs-Any/MainGenericRunner.java new file mode 100644 index 000000000000..ac1b4feaeb3a --- /dev/null +++ b/tests/link/dce/link-varargs-Any/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex); + } + } +} diff --git a/tests/link/dce/link-varargs-Any/link-varargs-Any.scala b/tests/link/dce/link-varargs-Any/link-varargs-Any.scala new file mode 100644 index 000000000000..dd64a0564235 --- /dev/null +++ b/tests/link/dce/link-varargs-Any/link-varargs-Any.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 170, classesWithReachableMethods = 49, reachableMethods = 210) + def main(args: Array[String]): Unit = { + foo("a", "b", "c") + } + + def foo(ns: Any*): Unit = { + System.out.println(42) + } +} diff --git a/tests/link/dce/link-varargs-AnyRef-empty.check b/tests/link/dce/link-varargs-AnyRef-empty.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-AnyRef-empty.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-AnyRef-empty/MainGenericRunner.java b/tests/link/dce/link-varargs-AnyRef-empty/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-AnyRef-empty/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-AnyRef-empty/Test.scala b/tests/link/dce/link-varargs-AnyRef-empty/Test.scala new file mode 100644 index 000000000000..cd5904553e00 --- /dev/null +++ b/tests/link/dce/link-varargs-AnyRef-empty/Test.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 172, classesWithReachableMethods = 55, reachableMethods = 177) + def main(args: Array[String]): Unit = { + foo() + } + + def foo(ns: AnyRef*): Unit = { + System.out.println(42) + } +} diff --git a/tests/link/dce/link-varargs-AnyRef.check b/tests/link/dce/link-varargs-AnyRef.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-AnyRef.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-AnyRef/MainGenericRunner.java b/tests/link/dce/link-varargs-AnyRef/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-AnyRef/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-AnyRef/Test.scala b/tests/link/dce/link-varargs-AnyRef/Test.scala new file mode 100644 index 000000000000..def3ad9e8458 --- /dev/null +++ b/tests/link/dce/link-varargs-AnyRef/Test.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 171, classesWithReachableMethods = 50, reachableMethods = 211) + def main(args: Array[String]): Unit = { + foo(new Object) + } + + def foo(ns: AnyRef*): Unit = { + System.out.println(42) + } +} diff --git a/tests/link/dce/link-varargs-int-empty.check b/tests/link/dce/link-varargs-int-empty.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-int-empty.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-int-empty/MainGenericRunner.java b/tests/link/dce/link-varargs-int-empty/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-int-empty/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-int-empty/Test.scala b/tests/link/dce/link-varargs-int-empty/Test.scala new file mode 100644 index 000000000000..75af6600b41a --- /dev/null +++ b/tests/link/dce/link-varargs-int-empty/Test.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 171, classesWithReachableMethods = 50, reachableMethods = 210) + def main(args: Array[String]): Unit = { + foo() + } + + def foo(ns: Int*): Unit = { + System.out.println(42) + } +} diff --git a/tests/link/dce/link-varargs-int.check b/tests/link/dce/link-varargs-int.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-int.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-int/MainGenericRunner.java b/tests/link/dce/link-varargs-int/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-int/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-int/Test.scala b/tests/link/dce/link-varargs-int/Test.scala new file mode 100644 index 000000000000..d6ad3446fbc2 --- /dev/null +++ b/tests/link/dce/link-varargs-int/Test.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 173, classesWithReachableMethods = 54, reachableMethods = 176) + def main(args: Array[String]): Unit = { + foo(1, 2, 3) + } + + def foo(ns: Int*): Unit = { + System.out.println(42) + } +} diff --git a/tests/link/dce/link-varargs-poly1.check b/tests/link/dce/link-varargs-poly1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-poly1.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-poly1/MainGenericRunner.java b/tests/link/dce/link-varargs-poly1/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-poly1/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-poly1/Test.scala b/tests/link/dce/link-varargs-poly1/Test.scala new file mode 100644 index 000000000000..0ea94307024f --- /dev/null +++ b/tests/link/dce/link-varargs-poly1/Test.scala @@ -0,0 +1,15 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 170, classesWithReachableMethods = 49, reachableMethods = 210) + def main(args: Array[String]): Unit = { + foo(1) + } + + def bar[T](x: T) = foo(x) + + def foo(ns: Any*): Unit = { + System.out.println(42) + } + +} diff --git a/tests/link/dce/link-varargs-poly2-empty.check b/tests/link/dce/link-varargs-poly2-empty.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-poly2-empty.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-poly2-empty/MainGenericRunner.java b/tests/link/dce/link-varargs-poly2-empty/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-poly2-empty/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-poly2-empty/Test.scala b/tests/link/dce/link-varargs-poly2-empty/Test.scala new file mode 100644 index 000000000000..67e65af68d78 --- /dev/null +++ b/tests/link/dce/link-varargs-poly2-empty/Test.scala @@ -0,0 +1,15 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 171, classesWithReachableMethods = 50, reachableMethods = 210) + def main(args: Array[String]): Unit = { + foo(1) + } + + def bar[T](x: T) = foo() + + def foo[U](ns: U*): Unit = { + System.out.println(42) + } + +} diff --git a/tests/link/dce/link-varargs-poly2.check b/tests/link/dce/link-varargs-poly2.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/dce/link-varargs-poly2.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/dce/link-varargs-poly2/MainGenericRunner.java b/tests/link/dce/link-varargs-poly2/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-poly2/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-poly2/Test.scala b/tests/link/dce/link-varargs-poly2/Test.scala new file mode 100644 index 000000000000..18013dbb5505 --- /dev/null +++ b/tests/link/dce/link-varargs-poly2/Test.scala @@ -0,0 +1,15 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 171, classesWithReachableMethods = 50, reachableMethods = 210) + def main(args: Array[String]): Unit = { + foo(1) + } + + def bar[T](x: T) = foo(x) + + def foo[U](ns: U*): Unit = { + System.out.println(42) + } + +} diff --git a/tests/link/dce/link-varargs-tupple.check b/tests/link/dce/link-varargs-tupple.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/dce/link-varargs-tupple/MainGenericRunner.java b/tests/link/dce/link-varargs-tupple/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/dce/link-varargs-tupple/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/dce/link-varargs-tupple/Test.scala b/tests/link/dce/link-varargs-tupple/Test.scala new file mode 100644 index 000000000000..d31426c14a75 --- /dev/null +++ b/tests/link/dce/link-varargs-tupple/Test.scala @@ -0,0 +1,8 @@ + +object Test { + def main(args: Array[String]): Unit = { + // scala.collection.immutable.Map[Int, Int]((1, 2), (3, 4), (5, 6)) + tupples((1, 2)) + } + def tupples[A, B](elems: (A, B)*): Unit = () +} diff --git a/tests/link/dce/link-while-loop-1.check b/tests/link/dce/link-while-loop-1.check new file mode 100644 index 000000000000..9dfcf39f5a78 --- /dev/null +++ b/tests/link/dce/link-while-loop-1.check @@ -0,0 +1,5 @@ +0 +1 +2 +3 +4 diff --git a/tests/link/dce/link-while-loop-1.scala b/tests/link/dce/link-while-loop-1.scala new file mode 100644 index 000000000000..606e15896a6a --- /dev/null +++ b/tests/link/dce/link-while-loop-1.scala @@ -0,0 +1,12 @@ +import scala.annotation.{internal, tailrec} + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 19, classesWithReachableMethods = 6, reachableMethods = 6) + def main(args: Array[String]): Unit = { + var i = 0 + while (i < 5) { + System.out.println(i) + i += 1 + } + } +} diff --git a/tests/link/dce/link-while-loop-2.check b/tests/link/dce/link-while-loop-2.check new file mode 100644 index 000000000000..9dfcf39f5a78 --- /dev/null +++ b/tests/link/dce/link-while-loop-2.check @@ -0,0 +1,5 @@ +0 +1 +2 +3 +4 diff --git a/tests/link/dce/link-while-loop-2.scala b/tests/link/dce/link-while-loop-2.scala new file mode 100644 index 000000000000..e4cbdfde52d8 --- /dev/null +++ b/tests/link/dce/link-while-loop-2.scala @@ -0,0 +1,12 @@ +import scala.annotation.internal + +object Test { + @internal.link.CallGraphBounds(reachableClasses = 19, classesWithReachableMethods = 6, reachableMethods = 6) + def main(args: Array[String]): Unit = { + var i = 0 + do { + System.out.println(i) + i += 1 + } while (i < 5) + } +} diff --git a/tests/link/specialize/link-specialize-method-1.check b/tests/link/specialize/link-specialize-method-1.check new file mode 100644 index 000000000000..ab4a19b21010 --- /dev/null +++ b/tests/link/specialize/link-specialize-method-1.check @@ -0,0 +1,3 @@ +42 +string +Foo diff --git a/tests/link/specialize/link-specialize-method-1.scala b/tests/link/specialize/link-specialize-method-1.scala new file mode 100644 index 000000000000..54ad95561aa8 --- /dev/null +++ b/tests/link/specialize/link-specialize-method-1.scala @@ -0,0 +1,31 @@ +import SpecializeUtils._ + +object Test { + def main(args: Array[String]): Unit = { + val foo = new Foo + System.out.println(foo.f(42)) + System.out.println(foo.f("string")) + System.out.println(foo.f(foo)) + + checkMethodExists(classOf[Foo], "f", List(classOf[Object]), classOf[Object], specialized = false) + checkMethodExists(classOf[Foo], "f", List(classOf[Int]), classOf[Int]) + checkMethodExists(classOf[Foo], "f", List(classOf[String]), classOf[String]) + checkMethodExists(classOf[Foo], "f", List(classOf[Foo]), classOf[Foo]) + } +} + +class Foo { + def f[T](e: T): T = e + + override def toString: String = "Foo" +} + +object SpecializeUtils { + + def checkMethodExists(cls: Class[_], name: String, params: List[Class[_]], ret: Class[_], specialized: Boolean = true): Unit = { + val nameMatch = if (specialized) name + "\\$spec\\d*" else name + val methods = cls.getDeclaredMethods + assert(methods.count(x => x.getName.matches(nameMatch) && x.getParameterTypes.toList == params && x.getReturnType == ret) == 1) + } + +} diff --git a/tests/link/specialize/link-specialize-method-2.check b/tests/link/specialize/link-specialize-method-2.check new file mode 100644 index 000000000000..ab4a19b21010 --- /dev/null +++ b/tests/link/specialize/link-specialize-method-2.check @@ -0,0 +1,3 @@ +42 +string +Foo diff --git a/tests/link/specialize/link-specialize-method-2.scala b/tests/link/specialize/link-specialize-method-2.scala new file mode 100644 index 000000000000..991e3a3d4d37 --- /dev/null +++ b/tests/link/specialize/link-specialize-method-2.scala @@ -0,0 +1,38 @@ +import SpecializeUtils._ + +object Test { + + def main(args: Array[String]): Unit = { + val foo = new Foo + System.out.println(foo.f(42)) + System.out.println(foo.f("string")) + System.out.println(foo.f(foo)) + + checkMethodExists(classOf[Foo], "f", List(classOf[Object]), classOf[Object], specialized = false) + checkMethodExists(classOf[Foo], "f", List(classOf[Int]), classOf[Int]) + checkMethodExists(classOf[Foo], "f", List(classOf[String]), classOf[String]) + checkMethodExists(classOf[Foo], "f", List(classOf[Foo]), classOf[Foo]) + + checkMethodExists(classOf[Foo], "g", List(classOf[Object]), classOf[Object], specialized = false) + checkMethodExists(classOf[Foo], "g", List(classOf[Int]), classOf[Int]) + checkMethodExists(classOf[Foo], "g", List(classOf[String]), classOf[String]) + checkMethodExists(classOf[Foo], "g", List(classOf[Foo]), classOf[Foo]) + } +} + +class Foo { + def f[T](e: T): T = g(e) + def g[T](e: T): T = e + + override def toString: String = "Foo" +} + +object SpecializeUtils { + + def checkMethodExists(cls: Class[_], name: String, params: List[Class[_]], ret: Class[_], specialized: Boolean = true): Unit = { + val nameMatch = if (specialized) name + "\\$spec\\d*" else name + val methods = cls.getDeclaredMethods + assert(methods.count(x => x.getName.matches(nameMatch) && x.getParameterTypes.toList == params && x.getReturnType == ret) == 1) + } + +} diff --git a/tests/link/specialize/link-specialize-method-3.check b/tests/link/specialize/link-specialize-method-3.check new file mode 100644 index 000000000000..05c910ede53c --- /dev/null +++ b/tests/link/specialize/link-specialize-method-3.check @@ -0,0 +1,2 @@ +Bar +Bar diff --git a/tests/link/specialize/link-specialize-method-3.scala b/tests/link/specialize/link-specialize-method-3.scala new file mode 100644 index 000000000000..b9ec59de26d0 --- /dev/null +++ b/tests/link/specialize/link-specialize-method-3.scala @@ -0,0 +1,35 @@ +import SpecializeUtils._ + +object Test { + def main(args: Array[String]): Unit = { + val foo = new Foo + System.out.println(foo.fun1(new Bar)) + System.out.println(foo.fun2(new Bar)) + + checkMethodExists(classOf[Foo], "fun1", List(classOf[Foo]), classOf[Foo], specialized = false) + checkMethodExists(classOf[Foo], "fun1", List(classOf[Bar]), classOf[Bar]) + + checkMethodExists(classOf[Foo], "fun2", List(classOf[Foo]), classOf[Foo], specialized = false) + } + +} + +class Foo { + def fun1[F <: Foo](x: F) = x // Specialized + def fun2(x: Foo) = x // Not specialized + +} + +class Bar extends Foo { + override def toString: String = "Bar" +} + +object SpecializeUtils { + + def checkMethodExists(cls: Class[_], name: String, params: List[Class[_]], ret: Class[_], specialized: Boolean = true): Unit = { + val nameMatch = if (specialized) name + "\\$spec\\d*" else name + val methods = cls.getDeclaredMethods + assert(methods.count(x => x.getName.matches(nameMatch) && x.getParameterTypes.toList == params && x.getReturnType == ret) == 1) + } + +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString.check new file mode 100644 index 000000000000..2739d724db3b --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString.check @@ -0,0 +1 @@ +1, 2, 3 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/Test.scala new file mode 100644 index 000000000000..202104713c3e --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + System.out.println(Array(1, 2, 3).mkString(", ")) + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-array-mkString/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/Test.scala new file mode 100644 index 000000000000..5795cdfdeca1 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/Test.scala @@ -0,0 +1,8 @@ +import scala.annotation.internal +import scala.collection.mutable + +object Test { + def main(args: Array[String]): Unit = { + mutable.HashMap[String, String]() + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-hashmap/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/Test.scala new file mode 100644 index 000000000000..e8d664f8ffb1 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/Test.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.List(1, 2, 3) + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-list-3/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/MainGenericRunner.java new file mode 100644 index 000000000000..ac1b4feaeb3a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/Test.scala new file mode 100644 index 000000000000..27978b534311 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.mutable.Set.empty[Int] + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-0/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/Test.scala new file mode 100644 index 000000000000..2255dc339da9 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/Test.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.mutable.Set[Int]() + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-1/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/Test.scala new file mode 100644 index 000000000000..e8587a9569c3 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/Test.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.mutable.Set[Int](1, 2, 2) + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-mutable-set-2/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/Test.scala new file mode 100644 index 000000000000..6dfaa2117076 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/Test.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Set[Int]() + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-1/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/Test.scala new file mode 100644 index 000000000000..a566331cdbee --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/Test.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Set[Int](1, 2, 2) + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-set-2/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/Test.scala new file mode 100644 index 000000000000..bca85d6bf3fd --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Vector.empty[Int] :+ 1 + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-2/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/Test.scala new file mode 100644 index 000000000000..941387f2d2fd --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Vector(1, 2, 3) + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-3/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/Test.scala new file mode 100644 index 000000000000..14f3912ea652 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/Test.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Vector[Int](1, 3, 42) :+ 1 + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/App.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-4/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5.check b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5.check new file mode 100644 index 000000000000..1c4872563001 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5.check @@ -0,0 +1,2 @@ +864 +46 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/MainGenericRunner.java b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/Test.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/Test.scala new file mode 100644 index 000000000000..19773ff26573 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/Test.scala @@ -0,0 +1,9 @@ +import scala.collection.immutable._ + +object Test { + def main(args: Array[String]): Unit = { + val vec = Vector[Int](1, 3, 42) + System.out.println(vec.map(0 until _).flatMap(x => x).sum) + System.out.println(vec.fold(0)(_ + _)) + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/OVERWRITES b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-compile/stdlib-link-vector-5/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-App/MainGenericRunner.java b/tests/link/stdlib-dce-fail-run/stdlib-link-App/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-App/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-App/Test.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-App/Test.scala new file mode 100644 index 000000000000..525c3de08885 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-App/Test.scala @@ -0,0 +1,4 @@ + +object Test extends App { + System.out.println(42) +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/OVERWRITES b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-App/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList.check b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/MainGenericRunner.java b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/Test.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/Test.scala new file mode 100644 index 000000000000..5a2aff46ef13 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/Test.scala @@ -0,0 +1,8 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + args.toList + } + +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/App.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/OVERWRITES b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-array-toList/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2.check b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/MainGenericRunner.java b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/Test.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/Test.scala new file mode 100644 index 000000000000..0f6f25e3752a --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/Test.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Map[Int, Int]((1, 2), (3, 4), (5, 6)) + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/OVERWRITES b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-map-2/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4.check b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/MainGenericRunner.java b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/Test.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/Test.scala new file mode 100644 index 000000000000..7184318b57b5 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/Test.scala @@ -0,0 +1,12 @@ + +object Test { + object TestSeq { + def unapplySeq(x: Int): Option[Seq[Int]] = Some(List(x, x, x)) + } + + def main(args: Array[String]): Unit = { + 42 match { + case TestSeq(42, x, 42) => System.out.println(x) + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/App.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/App.scala new file mode 100644 index 000000000000..b0f3a2afa531 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/App.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2010-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.compat.Platform.currentTime +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * Here, object `Main` inherits the `main` method of `App`. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * It should also be noted that the `main` method should not be overridden: + * the whole class body becomes the “main method”. + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @version 2.1, 15/02/2011 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + @deprecatedOverriding("executionStart should not be overridden", "2.11.0") + val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + @deprecatedOverriding("args should not be overridden", "2.11.0") + protected def args: Array[String] = _args + + private var _args: Array[String] = _ + + private val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("The delayedInit mechanism will disappear.", "2.11.0") + override def delayedInit(body: => Unit) { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + /* DISABELED FOR CALLGRAPH DCE + @deprecatedOverriding("main should not be overridden", "2.11.0") + def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + } + */ +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/Enumeration.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/OVERWRITES b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/util/Properties.scala b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/util/Properties.scala new file mode 100644 index 000000000000..8254f062619a --- /dev/null +++ b/tests/link/stdlib-dce-fail-run/stdlib-link-patmatch-4/scala/util/Properties.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala +package util + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +private[scala] trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + def envOrSome(name: String, alt: Option[String]) = envOrNone(name) orElse alt + + // for values based on propFilename, falling back to System properties + def scalaPropOrElse(name: String, alt: String): String = scalaPropOrNone(name).getOrElse(alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)).orElse(propOrNone("scala." + name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding. + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /* Various well-known properties. */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def javaSpecVersion = propOrEmpty("java.specification.version") + def javaSpecVendor = propOrEmpty("java.specification.vendor") + def javaSpecName = propOrEmpty("java.specification.name") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /* Some derived values. */ + /** Returns `true` iff the underlying operating system is a version of Microsoft Windows. */ + def isWin = osName startsWith "Windows" + // See http://mail.openjdk.java.net/pipermail/macosx-port-dev/2012-November/005148.html for + // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. + /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ + def isMac = osName startsWith "Mac OS X" + + /* Some runtime values. */ + private[scala] def isAvian = javaVmName contains "Avian" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Compares the given specification version to the specification version of the platform. + * + * @param version a specification version of the form "major.minor" + * @return `true` iff the specification version of the current runtime + * is equal to or higher than the version denoted by the given string. + * @throws NumberFormatException if the given string is not a version string + * + * @example {{{ + * // In this example, the runtime's Java specification is assumed to be at version 1.7. + * isJavaAtLeast("1.6") // true + * isJavaAtLeast("1.7") // true + * isJavaAtLeast("1.8") // false + * }}} + */ + def isJavaAtLeast(version: String): Boolean = { + def parts(x: String) = { + val i = x.indexOf('.') + if (i < 0) throw new NumberFormatException("Not a version: " + x) + (x.substring(0, i), x.substring(i+1, x.length)) + } + val (v, _v) = parts(version) + val (s, _s) = parts(javaSpecVersion) + s.toInt >= v.toInt && _s.toInt >= _v.toInt + } + + // provide a main method so version info can be obtained by running this + /* DISABELED FOR CALLGRAPH DCE + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } + */ +} diff --git a/tests/link/stdlib-dce/stdlib-link-3xqmark.check b/tests/link/stdlib-dce/stdlib-link-3xqmark.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-3xqmark.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/stdlib-dce/stdlib-link-list.check b/tests/link/stdlib-dce/stdlib-link-list.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-list/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-list/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-list/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-list/Test.scala b/tests/link/stdlib-dce/stdlib-link-list/Test.scala new file mode 100644 index 000000000000..94de904643ea --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-list/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + 1 :: scala.collection.immutable.List.empty[Int] + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-map.check b/tests/link/stdlib-dce/stdlib-link-map.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-map/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-map/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-map/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-map/Test.scala b/tests/link/stdlib-dce/stdlib-link-map/Test.scala new file mode 100644 index 000000000000..12ca4d78737f --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-map/Test.scala @@ -0,0 +1,8 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Map.empty[Int, Int] + scala.collection.immutable.Map[Int, Int]() + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-module-init.check b/tests/link/stdlib-dce/stdlib-link-module-init.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-module-init/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-module-init/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-module-init/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-module-init/Test.scala b/tests/link/stdlib-dce/stdlib-link-module-init/Test.scala new file mode 100644 index 000000000000..20a5fdf2943b --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-module-init/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + Predef // Just load Predef module + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-module-init/scala/Enumeration.scala b/tests/link/stdlib-dce/stdlib-link-module-init/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-module-init/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-module-init/scala/OVERWRITES b/tests/link/stdlib-dce/stdlib-link-module-init/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-module-init/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce/stdlib-link-module-init/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-module-init/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce/stdlib-link-mutable-set.check b/tests/link/stdlib-dce/stdlib-link-mutable-set.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-patmatch.check b/tests/link/stdlib-dce/stdlib-link-patmatch.check new file mode 100644 index 000000000000..573105485fa9 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-patmatch.check @@ -0,0 +1,4 @@ +21 +7 +42 +43 diff --git a/tests/link/stdlib-dce/stdlib-link-patmatch/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-patmatch/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-patmatch/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-patmatch/Test.scala b/tests/link/stdlib-dce/stdlib-link-patmatch/Test.scala new file mode 100644 index 000000000000..9223c670ef00 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-patmatch/Test.scala @@ -0,0 +1,34 @@ +import scala.annotation.internal + +object Test { + object Twice { + def unapply(x: Int): Option[Int] = if (x % 2 == 0) Some(x / 2) else None + } + + object Thrice { + def unapply(x: Int): Option[Int] = if (x % 3 == 0) Some(x / 3) else None + } + + object In2 { + def unapply(x: Int): Option[(Int, Int)] = if (x % 2 == 0) Some(x / 2, x / 2) else None + } + + case class Foo(x: Int) + + def main(args: Array[String]): Unit = { + 84 match { + case In2(Twice(x), Twice(Thrice(y))) => + System.out.println(x) + System.out.println(y) + case _ => + } + + val Twice(x) = 84 + System.out.println(x) + + Foo(43) match { + case Foo(x) => System.out.println(x) + case _ => + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-predef.check b/tests/link/stdlib-dce/stdlib-link-predef.check new file mode 100644 index 000000000000..4aaf59bb6139 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-predef.check @@ -0,0 +1,3 @@ +break +??? +42 diff --git a/tests/link/stdlib-dce/stdlib-link-predef/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-predef/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-predef/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-predef/Test.scala b/tests/link/stdlib-dce/stdlib-link-predef/Test.scala new file mode 100644 index 000000000000..545ee85b8458 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-predef/Test.scala @@ -0,0 +1,24 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + + // Loading Predef requires breaks + import scala.util.control.Breaks._ + breakable { + while (true) { + System.out.println("break") + break() + } + } + + + try { + ??? + } catch { + case _: NotImplementedError => System.out.println("???") + } + + println(42) + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-predef/scala/Enumeration.scala b/tests/link/stdlib-dce/stdlib-link-predef/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-predef/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-predef/scala/OVERWRITES b/tests/link/stdlib-dce/stdlib-link-predef/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-predef/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce/stdlib-link-predef/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-predef/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/stdlib-dce/stdlib-link-set.check b/tests/link/stdlib-dce/stdlib-link-set.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-set/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-set/MainGenericRunner.java new file mode 100644 index 000000000000..ac1b4feaeb3a --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-set/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-set/Test.scala b/tests/link/stdlib-dce/stdlib-link-set/Test.scala new file mode 100644 index 000000000000..a0695eddc740 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-set/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Set.empty[Int] + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-trycatch-unapply.check b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Bar.scala b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Bar.scala new file mode 100644 index 000000000000..4ea702381e76 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Bar.scala @@ -0,0 +1 @@ +class Bar extends Exception("BarException") diff --git a/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Foo.scala b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Foo.scala new file mode 100644 index 000000000000..2470224390e5 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Foo.scala @@ -0,0 +1 @@ +class Foo(e: Throwable) extends Exception(e) diff --git a/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/MainGenericRunner.java new file mode 100644 index 000000000000..aad9122fe31c --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex.getCause()); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Test.scala b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Test.scala new file mode 100644 index 000000000000..2e6cec0d1425 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-trycatch-unapply/Test.scala @@ -0,0 +1,16 @@ +import scala.annotation.internal + +object Test { + + object CausedBy { + def unapply(e: Throwable): Option[Throwable] = Option(e.getCause) + } + + def main(args: Array[String]): Unit = { + try { + throw new Foo(new Bar) + } catch { + case CausedBy(x: Bar) => System.out.println(42) + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-vector.check b/tests/link/stdlib-dce/stdlib-link-vector.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-vector/MainGenericRunner.java b/tests/link/stdlib-dce/stdlib-link-vector/MainGenericRunner.java new file mode 100644 index 000000000000..ac1b4feaeb3a --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-vector/MainGenericRunner.java @@ -0,0 +1,18 @@ +package scala.tools.nsc; + +// Override the MainGenericRunner for link deadcode elimination tests +public class MainGenericRunner { + public static void main(String[] args) { + try { + java.lang.Class.forName("Test").getMethod("main", String[].class).invoke(null, (Object) args); + } catch (ClassNotFoundException ex) { + throw new java.lang.RuntimeException(ex); + } catch (NoSuchMethodException ex) { + throw new java.lang.RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new java.lang.RuntimeException(ex); + } catch (java.lang.reflect.InvocationTargetException ex) { + throw new java.lang.RuntimeException(ex); + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-vector/Test.scala b/tests/link/stdlib-dce/stdlib-link-vector/Test.scala new file mode 100644 index 000000000000..ececf2c62249 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-vector/Test.scala @@ -0,0 +1,7 @@ +import scala.annotation.internal + +object Test { + def main(args: Array[String]): Unit = { + scala.collection.immutable.Vector.empty + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-vector/scala/Enumeration.scala b/tests/link/stdlib-dce/stdlib-link-vector/scala/Enumeration.scala new file mode 100644 index 000000000000..b81ee74d1586 --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-vector/scala/Enumeration.scala @@ -0,0 +1,292 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala + +import scala.collection.{ mutable, immutable, generic, SortedSetLike, AbstractSet } +import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the evaluation. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * @example {{{ + * object Main extends App { + * + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * } + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = thisenum.getClass.getField(MODULE_INSTANCE_NAME).get(null) + + /** The name of this enumeration. + */ + override def toString() = + ((getClass.getName stripSuffix MODULE_SUFFIX_STRING split '.').last split + Regex.quote(NAME_JOIN_STRING)).last + + /** The mapping from the integer used to identify values to the actual + * values. */ +// private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private val nmap: mutable.Map[Int, String] = ??? // new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = ??? /* { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } */ + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = ??? // vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = values.find(_.toString == s).getOrElse( + throw new NoSuchElementException(s"No value found for '$s'")) + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + private def populateNameMap() = { + val fields = getClass.getDeclaredFields + def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) + + // The list of possible Value methods: 0-args which return a conforming type + val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && + classOf[Value].isAssignableFrom(m.getReturnType) && + m.getDeclaringClass != classOf[Enumeration] && + isValDef(m)) + methods foreach { m => + val name = m.getName + // invoke method to obtain actual `Value` instance + val value = m.invoke(this).asInstanceOf[Value] + // verify that outer points to the correct Enumeration: ticket #3616. + if (value.outerEnum eq thisenum) { + val id = Int.unbox(classOf[Val] getMethod "id" invoke value) + // nmap += ((id, name)) + } + } + } + + /* Obtains the name for the value with id `i`. If no name is cached + * in `nmap`, it populates `nmap` using reflection. + */ + private def nameOf(i: Int): String = ??? // synchronized { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + +// assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) +// vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + else try thisenum.nameOf(i) + catch { case _: NoSuchElementException => "" } + + protected def readResolve(): AnyRef = ??? /* { + val enum = thisenum.readResolve().asInstanceOf[Enumeration] + if (enum.vmap == null) this + else enum.vmap(i) + }*/ + } + + /** An ordering by id for values of this set */ + object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends AbstractSet[Value] + with immutable.SortedSet[Value] + with SortedSetLike[Value, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def keysIteratorFrom(start: Value) = nnIds keysIteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def stringPrefix = thisenum + ".ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + } + + /** A factory object for value sets */ + object ValueSet { + import generic.CanBuildFrom + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set consisting of given elements */ + def apply(elems: Value*): ValueSet = (newBuilder ++= elems).result() + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def += (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + /** The implicit builder for value sets */ + implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] = + new CanBuildFrom[ValueSet, Value, ValueSet] { + def apply(from: ValueSet) = newBuilder + def apply() = newBuilder + } + } +} diff --git a/tests/link/stdlib-dce/stdlib-link-vector/scala/OVERWRITES b/tests/link/stdlib-dce/stdlib-link-vector/scala/OVERWRITES new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/link/stdlib-dce/stdlib-link-vector/scala/sys/SystemProperties.scala b/tests/link/stdlib-dce/stdlib-link-vector/scala/sys/SystemProperties.scala new file mode 100644 index 000000000000..5c770af5608d --- /dev/null +++ b/tests/link/stdlib-dce/stdlib-link-vector/scala/sys/SystemProperties.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package sys + +import scala.collection.{ mutable, Iterator } +import scala.collection.JavaConverters._ +import java.security.AccessControlException +import scala.language.implicitConversions + + +/** A bidirectional map wrapping the java System properties. + * Changes to System properties will be immediately visible in the map, + * and modifications made to the map will be immediately applied to the + * System properties. If a security manager is in place which prevents + * the properties from being read or written, the AccessControlException + * will be caught and discarded. + * @define Coll `collection.mutable.Map` + * @define coll mutable map + * + * @author Paul Phillips + * @version 2.9 + * @since 2.9 + */ +class SystemProperties +extends mutable.AbstractMap[String, String] + with mutable.Map[String, String] { + + override def empty = new SystemProperties + override def default(key: String): String = null + + def iterator: Iterator[(String, String)] = + wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty + def get(key: String) = + wrapAccess(Option(System.getProperty(key))) flatMap (x => x) + override def contains(key: String) = + wrapAccess(super.contains(key)) exists (x => x) + + def -= (key: String): this.type = { wrapAccess(System.clearProperty(key)) ; this } + def += (kv: (String, String)): this.type = { wrapAccess(System.setProperty(kv._1, kv._2)) ; this } + + def wrapAccess[T](body: => T): Option[T] = + try Some(body) catch { case _: AccessControlException => None } +} + +/** The values in SystemProperties can be used to access and manipulate + * designated system properties. See `scala.sys.Prop` for particulars. + * @example {{{ + * if (!headless.isSet) headless.enable() + * }}} + */ +object SystemProperties { + /** An unenforceable, advisory only place to do some synchronization when + * mutating system properties. + */ + def exclusively[T](body: => T) = this synchronized body + + implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this + // FIXME: A PolyParam ends up in a CallInfoWithContext + // private lazy val propertyHelp = mutable.Map[String, String]() + private def addHelp[P <: Prop[_]](p: P, helpText: String): P = { +// propertyHelp(p.key) = helpText + p + } + private def bool(key: String, helpText: String): BooleanProp = addHelp[BooleanProp]( + if (key startsWith "java.") BooleanProp.valueIsTrue(key) else BooleanProp.keyExists(key), + helpText + ) +// def help(key: String) = propertyHelp.getOrElse(key, "") + + // Todo: bring some sanity to the intersection of system properties aka "mutable + // state shared by everyone and everything" and the reality that there is no other + // mechanism for accomplishing some things on the jvm. + lazy val headless = bool("java.awt.headless", "system should not utilize a display device") + lazy val preferIPv4Stack = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets") + lazy val preferIPv6Addresses = bool("java.net.preferIPv6Addresses", "system should prefer IPv6 addresses") + lazy val noTraceSupression = bool("scala.control.noTraceSuppression", "scala should not suppress any stack trace creation") +} + diff --git a/tests/link/strawman-dce-failing/link-strawman-dce-list-1.check b/tests/link/strawman-dce-failing/link-strawman-dce-list-1.check new file mode 100644 index 000000000000..ce013625030b --- /dev/null +++ b/tests/link/strawman-dce-failing/link-strawman-dce-list-1.check @@ -0,0 +1 @@ +hello diff --git a/tests/link/strawman-dce-failing/link-strawman-dce-list-1.scala b/tests/link/strawman-dce-failing/link-strawman-dce-list-1.scala new file mode 100644 index 000000000000..f371637dcc8f --- /dev/null +++ b/tests/link/strawman-dce-failing/link-strawman-dce-list-1.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + println(strawman.collection.immutable.List.empty) + } +} diff --git a/tests/link/strawman-dce/link-strawman-dce-all.check b/tests/link/strawman-dce/link-strawman-dce-all.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link/strawman-dce/link-strawman-dce-all.check @@ -0,0 +1 @@ +42 diff --git a/tests/link/strawman-dce/link-strawman-dce-all.scala b/tests/link/strawman-dce/link-strawman-dce-all.scala new file mode 100644 index 000000000000..0674797e1801 --- /dev/null +++ b/tests/link/strawman-dce/link-strawman-dce-all.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + println(42) // Dummy println + } +} \ No newline at end of file diff --git a/tests/link/strawman-dce/link-strawman-dce-iterator-1.check b/tests/link/strawman-dce/link-strawman-dce-iterator-1.check new file mode 100644 index 000000000000..ce013625030b --- /dev/null +++ b/tests/link/strawman-dce/link-strawman-dce-iterator-1.check @@ -0,0 +1 @@ +hello diff --git a/tests/link/strawman-dce/link-strawman-dce-iterator-1.scala b/tests/link/strawman-dce/link-strawman-dce-iterator-1.scala new file mode 100644 index 000000000000..2eaeef2dd088 --- /dev/null +++ b/tests/link/strawman-dce/link-strawman-dce-iterator-1.scala @@ -0,0 +1,11 @@ + +object Test { + def main(args: Array[String]): Unit = { + println(new Foo().next) + } +} + +class Foo extends strawman.collection.Iterator[String] { + def hasNext = true + def next = "hello" +} \ No newline at end of file diff --git a/tests/link/strawman-specialize-failing/link-strawman-specialize-iterator-1.check b/tests/link/strawman-specialize-failing/link-strawman-specialize-iterator-1.check new file mode 100644 index 000000000000..ce013625030b --- /dev/null +++ b/tests/link/strawman-specialize-failing/link-strawman-specialize-iterator-1.check @@ -0,0 +1 @@ +hello diff --git a/tests/link/strawman-specialize-failing/link-strawman-specialize-iterator-1.scala b/tests/link/strawman-specialize-failing/link-strawman-specialize-iterator-1.scala new file mode 100644 index 000000000000..1a4ad6f3bd05 --- /dev/null +++ b/tests/link/strawman-specialize-failing/link-strawman-specialize-iterator-1.scala @@ -0,0 +1,11 @@ + +object Test { + def main(args: Array[String]): Unit = { + println(new Foo("hello").next) + } +} + +class Foo[T](e: T) extends strawman.collection.Iterator[T] { + def hasNext = true + def next: T = e +} \ No newline at end of file diff --git a/tests/link/strawman-specialize/link-strawman-specialize-none.check b/tests/link/strawman-specialize/link-strawman-specialize-none.check new file mode 100644 index 000000000000..ce013625030b --- /dev/null +++ b/tests/link/strawman-specialize/link-strawman-specialize-none.check @@ -0,0 +1 @@ +hello diff --git a/tests/link/strawman-specialize/link-strawman-specialize-none.scala b/tests/link/strawman-specialize/link-strawman-specialize-none.scala new file mode 100644 index 000000000000..1bfe115f690c --- /dev/null +++ b/tests/link/strawman-specialize/link-strawman-specialize-none.scala @@ -0,0 +1,6 @@ + +object Test { + def main(args: Array[String]): Unit = { + println("hello") + } +}