diff options
Diffstat (limited to 'src/compiler/scala')
89 files changed, 1506 insertions, 7148 deletions
diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 0ff098e1da..3a68794c97 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -27,10 +27,10 @@ trait Errors { } def CannotConvertManifestToTagWithoutScalaReflect(tpe: Type, manifestInScope: Tree) = { - val msg = s""" - |to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. - |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. - |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin + val msg = + sm"""to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. + |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""" throw new ReificationException(defaultErrorPosition, msg) } diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 39103b801e..731aab93b8 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -98,7 +98,7 @@ trait GenSymbols { def reifyFreeTerm(binding: Tree): Tree = reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") - val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) + val name = newTermName("" + nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) if (sym.isCapturedVariable) { assert(binding.isInstanceOf[Ident], showRaw(binding)) val capturedBinding = referenceCapturedVariable(sym) @@ -112,14 +112,14 @@ trait GenSymbols { reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) state.reificationIsConcrete = false - val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name) + val name: TermName = nme.REIFY_FREE_PREFIX append sym.name Reification(name, binding, mirrorBuildCall(nme.newFreeType, reify(sym.name.toString), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) } def reifySymDef(sym: Symbol): Tree = reifyIntoSymtab(sym) { sym => if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) - val name = newTermName(nme.REIFY_SYMDEF_PREFIX + sym.name) + val name: TermName = nme.REIFY_SYMDEF_PREFIX append sym.name def reifiedOwner = if (sym.owner.isLocatable) reify(sym.owner) else reifySymDef(sym.owner) Reification(name, Ident(sym), mirrorBuildCall(nme.newNestedSymbol, reifiedOwner, reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(sym.isClass))) } @@ -143,7 +143,7 @@ trait GenSymbols { val reification = reificode(sym) import reification.{name, binding} val tree = reification.tree updateAttachment ReifyBindingAttachment(binding) - state.symtab += (sym, name, tree) + state.symtab += (sym, name.toTermName, tree) } fromSymtab } diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 86ad23cd15..f60089c935 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -45,7 +45,9 @@ trait GenTrees { case global.EmptyTree => reifyMirrorObject(EmptyTree) case global.emptyValDef => - mirrorBuildSelect(nme.emptyValDef) + mirrorSelect(nme.emptyValDef) + case global.pendingSuperCall => + mirrorSelect(nme.pendingSuperCall) case FreeDef(_, _, _, _, _) => reifyNestedFreeDef(tree) case FreeRef(_, _) => @@ -101,7 +103,8 @@ trait GenTrees { case ReifiedTree(_, _, inlinedSymtab, rtree, _, _, _) => if (reifyDebug) println("inlining the splicee") // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' - inlinedSymtab.syms foreach (sym => if (sym.isLocalToReifee) assert(false, inlinedSymtab.symDef(sym))) + for (sym <- inlinedSymtab.syms if sym.isLocalToReifee) + abort("local free var, should have already been inlined by Metalevels: " + inlinedSymtab.symDef(sym)) state.symtab ++= inlinedSymtab rtree case tree => @@ -117,88 +120,102 @@ trait GenTrees { // unlike in `reifyBoundType` we can skip checking for `tpe` being local or not local w.r.t the reifee // a single check for a symbol of the bound term should be enough // that's because only Idents and Thises can be bound terms, and they cannot host complex types - private def reifyBoundTerm(tree: Tree): Tree = tree match { - case tree @ This(_) if tree.symbol == NoSymbol => - throw new Error("unexpected: bound term that doesn't have a symbol: " + showRaw(tree)) - case tree @ This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass && !tree.symbol.isLocalToReifee => - val sym = tree.symbol - if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) - if (reifyDebug) println("Free: " + sym) - mirrorBuildCall(nme.Ident, reifyFreeTerm(This(sym))) - case tree @ This(_) if !tree.symbol.isLocalToReifee => - if (reifyDebug) println("This for %s, reified as This".format(tree.symbol)) - mirrorBuildCall(nme.This, reify(tree.symbol)) - case tree @ This(_) if tree.symbol.isLocalToReifee => - mirrorCall(nme.This, reify(tree.qual)) - case tree @ Ident(_) if tree.symbol == NoSymbol => - // this sometimes happens, e.g. for binds that don't have a body - // or for untyped code generated during previous phases - // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?") - mirrorCall(nme.Ident, reify(tree.name)) - case tree @ Ident(_) if !tree.symbol.isLocalToReifee => - if (tree.symbol.isVariable && tree.symbol.owner.isTerm) { - captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reification here. - mirrorCall(nme.Select, mirrorBuildCall(nme.Ident, reify(tree.symbol)), reify(nme.elem)) - } else { - mirrorBuildCall(nme.Ident, reify(tree.symbol)) - } - case tree @ Ident(_) if tree.symbol.isLocalToReifee => - mirrorCall(nme.Ident, reify(tree.name)) - case _ => - throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + private def reifyBoundTerm(tree: Tree): Tree = { + val sym = tree.symbol + + tree match { + case This(qual) => + assert(sym != NoSymbol, "unexpected: bound term that doesn't have a symbol: " + showRaw(tree)) + if (sym.isLocalToReifee) + mirrorCall(nme.This, reify(qual)) + else if (sym.isClass && !sym.isModuleClass) { + if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) + if (reifyDebug) println("Free: " + sym) + mirrorBuildCall(nme.Ident, reifyFreeTerm(This(sym))) + } + else { + if (reifyDebug) println("This for %s, reified as This".format(sym)) + mirrorBuildCall(nme.This, reify(sym)) + } + + case Ident(name) => + if (sym == NoSymbol) { + // this sometimes happens, e.g. for binds that don't have a body + // or for untyped code generated during previous phases + // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?") + mirrorCall(nme.Ident, reify(name)) + } + else if (!sym.isLocalToReifee) { + if (sym.isVariable && sym.owner.isTerm) { + captureVariable(sym) // Note order dependency: captureVariable needs to come before reification here. + mirrorCall(nme.Select, mirrorBuildCall(nme.Ident, reify(sym)), reify(nme.elem)) + } + else mirrorBuildCall(nme.Ident, reify(sym)) + } + else mirrorCall(nme.Ident, reify(name)) + + case Select(qual, name) => + if (sym == NoSymbol || sym.name == name) + reifyProduct(tree) + else + reifyProduct(Select(qual, sym.name)) + + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } } private def reifyBoundType(tree: Tree): Tree = { + val sym = tree.symbol + val tpe = tree.tpe + def reifyBoundType(tree: Tree): Tree = { - if (tree.tpe == null) - throw new Error("unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) + assert(tpe != null, "unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) // if a symbol or a type of the scrutinee are local to reifee // (e.g. point to a locally declared class or to a path-dependent thingie that depends on a local variable) // then we can reify the scrutinee as a symless AST and that will definitely be hygienic // why? because then typechecking of a scrutinee doesn't depend on the environment external to the quasiquote // otherwise we need to reify the corresponding type - if (tree.symbol.isLocalToReifee || tree.tpe.isLocalToReifee) + if (sym.isLocalToReifee || tpe.isLocalToReifee) reifyProduct(tree) else { - val sym = tree.symbol - val tpe = tree.tpe if (reifyDebug) println("reifying bound type %s (underlying type is %s)".format(sym, tpe)) if (tpe.isSpliceable) { val spliced = spliceType(tpe) + if (spliced == EmptyTree) { if (reifyDebug) println("splicing failed: reify as is") mirrorBuildCall(nme.TypeTree, reify(tpe)) - } else { - spliced match { - case TypeRefToFreeType(freeType) => - if (reifyDebug) println("splicing returned a free type: " + freeType) - Ident(freeType) - case _ => - if (reifyDebug) println("splicing succeeded: " + spliced) - mirrorBuildCall(nme.TypeTree, spliced) - } } - } else { - if (sym.isLocatable) { - if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) - mirrorBuildCall(nme.Ident, reify(sym)) - } else { - if (reifyDebug) println("tpe is not locatable: reify as TypeTree(%s)".format(tpe)) - mirrorBuildCall(nme.TypeTree, reify(tpe)) + else spliced match { + case TypeRefToFreeType(freeType) => + if (reifyDebug) println("splicing returned a free type: " + freeType) + Ident(freeType) + case _ => + if (reifyDebug) println("splicing succeeded: " + spliced) + mirrorBuildCall(nme.TypeTree, spliced) } } + else if (sym.isLocatable) { + if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) + mirrorBuildCall(nme.Ident, reify(sym)) + } + else { + if (reifyDebug) println("tpe is not locatable: reify as TypeTree(%s)".format(tpe)) + mirrorBuildCall(nme.TypeTree, reify(tpe)) + } } } tree match { - case Select(_, _) => - reifyBoundType(tree) - case SelectFromTypeTree(_, _) => - reifyBoundType(tree) - case Ident(_) => + case Select(qual, name) if name != sym.name => + reifyBoundType(Select(qual, sym.name)) + + case Select(_, _) | SelectFromTypeTree(_, _) | Ident(_) => reifyBoundType(tree) + case _ => throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) } diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index e2275f79ff..e0570d61f2 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -30,41 +30,32 @@ trait GenUtils { def call(fname: String, args: Tree*): Tree = Apply(termPath(fname), args.toList) - def mirrorSelect(name: String): Tree = - termPath(nme.UNIVERSE_PREFIX + name) + def mirrorSelect(name: String): Tree = termPath(nme.UNIVERSE_PREFIX + name) + def mirrorSelect(name: TermName): Tree = mirrorSelect(name.toString) - def mirrorBuildSelect(name: String): Tree = - termPath(nme.UNIVERSE_BUILD_PREFIX + name) - - def mirrorMirrorSelect(name: String): Tree = - termPath(nme.MIRROR_PREFIX + name) + def mirrorMirrorSelect(name: TermName): Tree = + termPath("" + nme.MIRROR_PREFIX + name) def mirrorCall(name: TermName, args: Tree*): Tree = - call("" + (nme.UNIVERSE_PREFIX append name), args: _*) - - def mirrorCall(name: String, args: Tree*): Tree = - call(nme.UNIVERSE_PREFIX + name, args: _*) + call("" + nme.UNIVERSE_PREFIX + name, args: _*) def mirrorBuildCall(name: TermName, args: Tree*): Tree = - call("" + (nme.UNIVERSE_BUILD_PREFIX append name), args: _*) - - def mirrorBuildCall(name: String, args: Tree*): Tree = - call(nme.UNIVERSE_BUILD_PREFIX + name, args: _*) + call("" + nme.UNIVERSE_BUILD_PREFIX + name, args: _*) def mirrorMirrorCall(name: TermName, args: Tree*): Tree = - call("" + (nme.MIRROR_PREFIX append name), args: _*) - - def mirrorMirrorCall(name: String, args: Tree*): Tree = - call(nme.MIRROR_PREFIX + name, args: _*) + call("" + nme.MIRROR_PREFIX + name, args: _*) def mirrorFactoryCall(value: Product, args: Tree*): Tree = mirrorFactoryCall(value.productPrefix, args: _*) - def mirrorFactoryCall(prefix: String, args: Tree*): Tree = - mirrorCall(prefix, args: _*) + def mirrorFactoryCall(prefix: TermName, args: Tree*): Tree = + mirrorCall("" + prefix, args: _*) + + def scalaFactoryCall(name: TermName, args: Tree*): Tree = + call(s"scala.$name.apply", args: _*) def scalaFactoryCall(name: String, args: Tree*): Tree = - call("scala." + name + ".apply", args: _*) + scalaFactoryCall(name: TermName, args: _*) def mkList(args: List[Tree]): Tree = scalaFactoryCall("collection.immutable.List", args: _*) diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index f31c3d4755..75384ddce1 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -254,7 +254,7 @@ trait Reshape { val flags1 = (flags0 & GetterFlags) & ~(STABLE | ACCESSOR | METHOD) val mods1 = Modifiers(flags1, privateWithin0, annotations0) setPositions mods0.positions val mods2 = toPreTyperModifiers(mods1, ddef.symbol) - ValDef(mods2, name1, tpt0, extractRhs(rhs0)) + ValDef(mods2, name1.toTermName, tpt0, extractRhs(rhs0)) } private def trimAccessors(deff: Tree, stats: List[Tree]): List[Tree] = { @@ -293,7 +293,7 @@ trait Reshape { } val mods2 = toPreTyperModifiers(mods1, vdef.symbol) val name1 = nme.dropLocalSuffix(name) - val vdef1 = ValDef(mods2, name1, tpt, rhs) + val vdef1 = ValDef(mods2, name1.toTermName, tpt, rhs) if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1)) Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are now out of sync case ddef: DefDef if !ddef.mods.isLazy => diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index 50bd309b52..254cb02ee6 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -251,6 +251,8 @@ trait Extractors { object BoundTerm { def unapply(tree: Tree): Option[Tree] = tree match { + case Select(_, name) if name.isTermName => + Some(tree) case Ident(name) if name.isTermName => Some(tree) case This(_) => diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index 97ec479a6c..86e50e0a68 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -75,10 +75,10 @@ trait NodePrinters { printout += universe.trim if (mirrorIsUsed) printout += mirror.replace("Mirror[", "scala.reflect.api.Mirror[").trim val imports = scala.collection.mutable.ListBuffer[String](); - imports += nme.UNIVERSE_SHORT + imports += nme.UNIVERSE_SHORT.toString // if (buildIsUsed) imports += nme.build - if (mirrorIsUsed) imports += nme.MIRROR_SHORT - if (flagsAreUsed) imports += nme.Flag + if (mirrorIsUsed) imports += nme.MIRROR_SHORT.toString + if (flagsAreUsed) imports += nme.Flag.toString printout += s"""import ${imports map (_ + "._") mkString ", "}""" val name = if (isExpr) "tree" else "tpe" diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index cf3b5f949b..3b8ae202f6 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -56,8 +56,6 @@ import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} * - `usejavacp`, * - `failonerror`, * - `scalacdebugging`, - * - `assemname`, - * - `assemrefs`. * * It also takes the following parameters as nested elements: * - `src` (for `srcdir`), @@ -100,7 +98,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Defines valid values for the `target` property. */ object Target extends PermissibleValue { - val values = List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil") + val values = List("jvm-1.5", "jvm-1.6", "jvm-1.7") } /** Defines valid values for the `deprecation` and `unchecked` properties. */ @@ -170,11 +168,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Indicates whether compilation errors will fail the build; defaults to true. */ protected var failonerror: Boolean = true - // Name of the output assembly (only relevant with -target:msil) - protected var assemname: Option[String] = None - // List of assemblies referenced by the program (only relevant with -target:msil) - protected var assemrefs: Option[String] = None - /** Prints out the files being compiled by the scalac ant task * (not only the number of files). */ protected var scalacDebugging: Boolean = false @@ -421,9 +414,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared { * @param input The specified flag */ def setScalacdebugging(input: Boolean) { scalacDebugging = input } - def setAssemname(input: String) { assemname = Some(input) } - def setAssemrefs(input: String) { assemrefs = Some(input) } - /** Sets the `compilerarg` as a nested compilerarg Ant parameter. * @return A compiler argument to be configured. */ def createCompilerArg(): ImplementationSpecificArgument = { @@ -616,9 +606,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared { if (!unchecked.isEmpty) settings.unchecked.value = unchecked.get if (!usejavacp.isEmpty) settings.usejavacp.value = usejavacp.get - if (!assemname.isEmpty) settings.assemname.value = assemname.get - if (!assemrefs.isEmpty) settings.assemrefs.value = assemrefs.get - val jvmargs = scalacCompilerArgs.getArgs filter (_ startsWith "-J") if (!jvmargs.isEmpty) settings.jvmargs.value = jvmargs.toList val defines = scalacCompilerArgs.getArgs filter (_ startsWith "-D") diff --git a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala index 9cdf484080..d5545fe76a 100644 --- a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala +++ b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala @@ -80,7 +80,7 @@ class ScalacFork extends ScalaMatchingTask with ScalacShared with TaskArgs { private def createMapper() = { val mapper = new GlobPatternMapper() - val extension = if (isMSIL) "*.msil" else "*.class" + val extension = "*.class" mapper setTo extension mapper setFrom "*.scala" @@ -104,9 +104,6 @@ class ScalacFork extends ScalaMatchingTask with ScalacShared with TaskArgs { sourcePath foreach (settings.sourcepath = _) settings.extraParams = extraArgsFlat - if (isMSIL) - settings.sourcedir = sourceDir - val mapper = createMapper() val includedFiles: Array[File] = diff --git a/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala b/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala index 6bb1aaa306..b061bcf7fb 100644 --- a/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala +++ b/src/compiler/scala/tools/ant/sabbus/TaskArgs.scala @@ -98,6 +98,4 @@ trait TaskArgs extends CompilationPathProperty { val parts = a.getParts if(parts eq null) Seq[String]() else parts.toSeq } - - def isMSIL = compTarget exists (_ == "msil") } diff --git a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl index f1c6c52785..84ccaba749 100644 --- a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl @@ -102,6 +102,9 @@ if [[ -n "$cygwin" ]]; then format=windows fi SCALA_HOME="$(cygpath --$format "$SCALA_HOME")" + if [[ -n "$JAVA_HOME" ]]; then + JAVA_HOME="$(cygpath --$format "$JAVA_HOME")" + fi TOOL_CLASSPATH="$(cygpath --path --$format "$TOOL_CLASSPATH")" elif [[ -n "$mingw" ]]; then SCALA_HOME="$(cmd //c echo "$SCALA_HOME")" diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 663fbeceb0..a2108b8ced 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -23,7 +23,7 @@ trait CompilationUnits { self: Global => /** One unit of compilation that has been submitted to the compiler. * It typically corresponds to a single file of source code. It includes * error-reporting hooks. */ - class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { + class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { self => /** the fresh name creator */ val fresh: FreshNameCreator = new FreshNameCreator.Default @@ -47,7 +47,23 @@ trait CompilationUnits { self: Global => /** Synthetic definitions generated by namer, eliminated by typer. */ - val synthetics = mutable.HashMap[Symbol, Tree]() + object synthetics { + private val map = mutable.HashMap[Symbol, Tree]() + def update(sym: Symbol, tree: Tree) { + debuglog(s"adding synthetic ($sym, $tree) to $self") + map.update(sym, tree) + } + def -=(sym: Symbol) { + debuglog(s"removing synthetic $sym from $self") + map -= sym + } + def get(sym: Symbol): Option[Tree] = logResultIf[Option[Tree]](s"found synthetic for $sym in $self", _.isDefined) { + map get sym + } + def keys: Iterable[Symbol] = map.keys + def clear(): Unit = map.clear() + override def toString = map.toString + } /** things to check at end of compilation unit */ val toCheck = new ListBuffer[() => Unit] diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index f79990d526..e4c250a3d9 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -56,9 +56,6 @@ class StandardCompileServer extends SocketServer { (totalMemory - freeMemory).toDouble / maxMemory.toDouble > MaxCharge } - protected def newOfflineCompilerCommand(arguments: List[String], settings: FscSettings): OfflineCompilerCommand = - new OfflineCompilerCommand(arguments, settings) - /** Problematically, Settings are only considered equal if every setting * is exactly equal. In fsc this immediately breaks down because the randomly * chosen temporary outdirs differ between client and server. Among other @@ -91,7 +88,7 @@ class StandardCompileServer extends SocketServer { val args = input.split("\0", -1).toList val newSettings = new FscSettings(fscError) this.verbose = newSettings.verbose.value - val command = newOfflineCompilerCommand(args, newSettings) + val command = new OfflineCompilerCommand(args, newSettings) info("Settings after normalizing paths: " + newSettings) printMemoryStats() diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 397e6c42d7..34d5d10cbf 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -23,8 +23,8 @@ import ast.parser._ import typechecker._ import transform._ import backend.icode.{ ICodes, GenICode, ICodeCheckers } -import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform } -import backend.jvm.{GenJVM, GenASM} +import backend.{ ScalaPrimitives, Platform, JavaPlatform } +import backend.jvm.GenASM import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination } import backend.icode.analysis._ import scala.language.postfixOps @@ -77,8 +77,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) type ThisPlatform = Platform { val global: Global.this.type } lazy val platform: ThisPlatform = - if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform - else new { val global: Global.this.type = Global.this } with JavaPlatform + new { val global: Global.this.type = Global.this } with JavaPlatform type PlatformClassPath = ClassPath[platform.BinaryRepr] type OptClassPath = Option[PlatformClassPath] @@ -594,13 +593,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with DeadCodeElimination - // phaseName = "jvm", FJBG-based version - object genJVM extends { - val global: Global.this.type = Global.this - val runsAfter = List("dce") - val runsRightAfter = None - } with GenJVM - // phaseName = "jvm", ASM-based version object genASM extends { val global: Global.this.type = Global.this @@ -620,7 +612,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) object terminal extends { val global: Global.this.type = Global.this val phaseName = "terminal" - val runsAfter = List("jvm", "msil") + val runsAfter = List("jvm") val runsRightAfter = None } with SubComponent { private var cache: Option[GlobalPhase] = None @@ -1314,7 +1306,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val closelimPhase = phaseNamed("closelim") val dcePhase = phaseNamed("dce") // val jvmPhase = phaseNamed("jvm") - // val msilPhase = phaseNamed("msil") def runIsAt(ph: Phase) = globalPhase.id == ph.id def runIsAtOptimiz = { @@ -1675,14 +1666,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } }) } - // In order to not outright break code which overrides onlyPresentation (like sbt 0.7.5.RC0) - // I restored and deprecated it. That would be enough to avoid the compilation - // failure, but the override wouldn't accomplish anything. So now forInteractive - // and forScaladoc default to onlyPresentation, which is the same as defaulting - // to false except in old code. The downside is that this leaves us calling a - // deprecated method: but I see no simple way out, so I leave it for now. - // def forJVM = settings.target.value startsWith "jvm" - override def forMSIL = settings.target.value startsWith "msil" def forInteractive = false def forScaladoc = false def createJavadoc = false diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala index 5a3ea56f67..a4b22b0e11 100644 --- a/src/compiler/scala/tools/nsc/Main.scala +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -9,7 +9,6 @@ import java.io.File import File.pathSeparator import scala.tools.nsc.interactive.{ RefinedBuildManager, SimpleBuildManager } import scala.tools.nsc.io.AbstractFile -import Properties.msilLibPath /** The main class for NSC, a compiler for the programming * language Scala. @@ -60,11 +59,7 @@ object Main extends Driver with EvalLoop { } false } - else { - if (settings.target.value == "msil") - msilLibPath foreach (x => settings.assemrefs.value += (pathSeparator + x)) - true - } + else true override def newCompiler(): Global = if (settings.Yrangepos.value) new Global(settings, reporter) with interactive.RangePositions diff --git a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala index 8a3c531ff0..2f4975e681 100644 --- a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala @@ -26,8 +26,8 @@ class OfflineCompilerCommand(arguments: List[String], settings: FscSettings) ext // instead of whatever it's supposed to be doing. val baseDirectory = { val pwd = System.getenv("PWD") - if (pwd != null && !isWin) Directory(pwd) - else Directory.Current getOrElse Directory("/") + if (pwd == null || isWin) Directory.Current getOrElse Directory("/") + else Directory(pwd) } currentDir.value = baseDirectory.path } diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala index 570d5572d6..feb4ded2f2 100644 --- a/src/compiler/scala/tools/nsc/Properties.scala +++ b/src/compiler/scala/tools/nsc/Properties.scala @@ -16,9 +16,6 @@ object Properties extends scala.util.PropertiesTrait { def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ") def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ") - // settings based on system properties - def msilLibPath = propOrNone("msil.libpath") - // derived values def isEmacsShell = propOrEmpty("env.emacs") != "" } diff --git a/src/compiler/scala/tools/nsc/ScalaDoc.scala b/src/compiler/scala/tools/nsc/ScalaDoc.scala index ba434bc797..14b76b53b3 100644 --- a/src/compiler/scala/tools/nsc/ScalaDoc.scala +++ b/src/compiler/scala/tools/nsc/ScalaDoc.scala @@ -10,7 +10,6 @@ import java.io.File.pathSeparator import scala.tools.nsc.doc.DocFactory import scala.tools.nsc.reporters.ConsoleReporter import scala.reflect.internal.util.FakePos -import Properties.msilLibPath /** The main class for scaladoc, a front-end for the Scala compiler * that generates documentation from source files. @@ -42,12 +41,8 @@ class ScalaDoc { reporter.warning(null, "Phases are restricted when using Scaladoc") else if (docSettings.help.value || !hasFiles) reporter.echo(command.usageMsg) - else try { - if (docSettings.target.value == "msil") - msilLibPath foreach (x => docSettings.assemrefs.value += (pathSeparator + x)) - - new DocFactory(reporter, docSettings) document command.files - } + else + try { new DocFactory(reporter, docSettings) document command.files } catch { case ex @ FatalError(msg) => if (docSettings.debug.value) ex.printStackTrace() diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala index 77acbba056..e7bd5da9dd 100644 --- a/src/compiler/scala/tools/nsc/ast/Positions.scala +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -20,7 +20,7 @@ trait Positions extends scala.reflect.internal.Positions { // When we prune due to encountering a position, traverse the // pruned children so we can warn about those lacking positions. t.children foreach { c => - if ((c eq EmptyTree) || (c eq emptyValDef)) () + if (!c.canHaveAttrs) () else if (c.pos == NoPosition) { reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) inform("parent: " + treeSymStatus(t)) diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index e8bc932bf5..1c6bba19b3 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -30,24 +30,17 @@ trait TreeDSL { def returning[T](x: T)(f: T => Unit): T = util.returning(x)(f) object LIT extends (Any => Literal) { + def typed(x: Any) = apply(x) setType ConstantType(Constant(x)) def apply(x: Any) = Literal(Constant(x)) def unapply(x: Any) = condOpt(x) { case Literal(Constant(value)) => value } } - // You might think these could all be vals, but empirically I have found that - // at least in the case of UNIT the compiler breaks if you re-use trees. - // However we need stable identifiers to have attractive pattern matching. - // So it's inconsistent until I devise a better way. - val TRUE = LIT(true) - val FALSE = LIT(false) - val ZERO = LIT(0) - def NULL = LIT(null) - def UNIT = LIT(()) - - // for those preferring boring, predictable lives, without the thrills of tree-sharing - // (but with the perk of typed trees) - def TRUE_typed = LIT(true) setType ConstantType(Constant(true)) - def FALSE_typed = LIT(false) setType ConstantType(Constant(false)) + // Boring, predictable trees. + def TRUE = LIT typed true + def FALSE = LIT typed false + def ZERO = LIT(0) + def NULL = LIT(null) + def UNIT = LIT(()) object WILD { def empty = Ident(nme.WILDCARD) @@ -106,6 +99,10 @@ trait TreeDSL { def DOT(sym: Symbol) = SelectStart(Select(target, sym)) /** Assignment */ + // !!! This method is responsible for some tree sharing, but a diligent + // reviewer pointed out that we shouldn't blindly duplicate these trees + // as there might be DefTrees nested beneath them. It's not entirely + // clear how to proceed, so for now it retains the non-duplicating behavior. def ===(rhs: Tree) = Assign(target, rhs) /** Methods for sequences **/ @@ -181,7 +178,7 @@ trait TreeDSL { self: VODDStart => type ResultTreeType = ValDef - def mkTree(rhs: Tree): ValDef = ValDef(mods, name, tpt, rhs) + def mkTree(rhs: Tree): ValDef = ValDef(mods, name.toTermName, tpt, rhs) } trait DefCreator { self: VODDStart => diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 1adcf46958..af874ed28c 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -60,33 +60,15 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { // This can't be "Annotated(New(UncheckedClass), expr)" because annotations // are very picky about things and it crashes the compiler with "unexpected new". - Annotated(New(scalaDot(UncheckedClass.name), ListOfNil), expr) + Annotated(New(scalaDot(UncheckedClass.name), Nil), expr) } // Builds a tree of the form "{ lhs = rhs ; lhs }" def mkAssignAndReturn(lhs: Symbol, rhs: Tree): Tree = { - val lhsRef = mkUnattributedRef(lhs) + def lhsRef = if (lhs.owner.isClass) Select(This(lhs.owner), lhs) else Ident(lhs) Block(Assign(lhsRef, rhs) :: Nil, lhsRef) } - def mkModuleVarDef(accessor: Symbol) = { - val inClass = accessor.owner.isClass - val extraFlags = if (inClass) PrivateLocal | SYNTHETIC else 0 - - val mval = ( - accessor.owner.newVariable(nme.moduleVarName(accessor.name), accessor.pos.focus, MODULEVAR | extraFlags) - setInfo accessor.tpe.finalResultType - addAnnotation VolatileAttr - ) - if (inClass) - mval.owner.info.decls enter mval - - ValDef(mval) - } - - def mkModuleAccessDef(accessor: Symbol, msym: Symbol) = - DefDef(accessor, Select(This(msym.owner), msym)) - def newModule(accessor: Symbol, tpe: Type) = { val ps = tpe.typeSymbol.primaryConstructor.info.paramTypes if (ps.isEmpty) New(tpe) @@ -210,7 +192,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { */ private def mkPackedValDef(expr: Tree, owner: Symbol, name: Name): (ValDef, () => Ident) = { val packedType = typer.packedType(expr, owner) - val sym = owner.newValue(name, expr.pos.makeTransparent, SYNTHETIC) setInfo packedType + val sym = owner.newValue(name.toTermName, expr.pos.makeTransparent, SYNTHETIC) setInfo packedType (ValDef(sym, expr), () => Ident(sym) setPos sym.pos.focus setType expr.tpe) } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index c78c40dcd3..0e3e2fe644 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -56,7 +56,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * The class `C` is stored as a tree attachment. */ case class InjectDerivedValue(arg: Tree) - extends SymTree + extends SymTree with TermTree class PostfixSelect(qual: Tree, name: Name) extends Select(qual, name) @@ -65,6 +65,13 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => // --- factory methods ---------------------------------------------------------- + /** Factory method for a primary constructor super call `super.<init>(args_1)...(args_n)` + */ + def PrimarySuperCall(argss: List[List[Tree]]): Tree = argss match { + case Nil => Apply(gen.mkSuperInitCall, Nil) + case xs :: rest => rest.foldLeft(Apply(gen.mkSuperInitCall, xs): Tree)(Apply.apply) + } + /** Generates a template with constructor corresponding to * * constrmods (vparams1_) ... (vparams_n) preSuper { presupers } @@ -82,7 +89,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * body * } */ - def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): Template = { + def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = { /* Add constructor to template */ // create parameters for <init> as synthetic trees. @@ -104,7 +111,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => rhs = EmptyTree ) } - val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = Modifiers(PRESUPER)) } + val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = vdef.mods | PRESUPER) } val constrs = { if (constrMods hasFlag TRAIT) { @@ -117,9 +124,16 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) vparamss1 = List() :: vparamss1; val superRef: Tree = atPos(superPos)(gen.mkSuperInitCall) - val superCall = (superRef /: argss) (Apply.apply) + val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass + // this requires knowing which of the parents is a type macro and which is not + // and that's something that cannot be found out before typer + // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) + // this means that we don't know what will be the arguments of the super call + // therefore here we emit a dummy which gets populated when the template is named and typechecked List( - atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( + // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)` + // is it going to be a problem that we can no longer include the `argss`? + atPos(wrappingPos(superPos, lvdefs)) ( DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } @@ -137,11 +151,10 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * @param constrMods the modifiers for the class constructor, i.e. as in `class C private (...)` * @param vparamss the value parameters -- if they have symbols they * should be owned by `sym` - * @param argss the supercall arguments * @param body the template statements without primary constructor * and value parameter fields. */ - def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): ClassDef = { + def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): ClassDef = { // "if they have symbols they should be owned by `sym`" assert( mforall(vparamss)(p => (p.symbol eq NoSymbol) || (p.symbol.owner == sym)), @@ -151,7 +164,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => ClassDef(sym, Template(sym.info.parents map TypeTree, if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym), - constrMods, vparamss, argss, body, superPos)) + constrMods, vparamss, body, superPos)) } // --- subcomponents -------------------------------------------------- @@ -324,6 +337,8 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => else super.transform { tree match { + case tree if !tree.canHaveAttrs => + tree case tpt: TypeTree => if (tpt.original != null) transform(tpt.original) @@ -337,8 +352,6 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => transform(fn) case This(_) if tree.symbol != null && tree.symbol.isPackageClass => tree - case EmptyTree => - tree case _ => val dupl = tree.duplicate if (tree.hasSymbolField && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel)) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f430f1fc34..76d7af7cc6 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -701,10 +701,10 @@ self => tree match { case Ident(name) => removeAsPlaceholder(name) - makeParam(name, TypeTree() setPos o2p(tree.pos.endOrPoint)) + makeParam(name.toTermName, TypeTree() setPos o2p(tree.pos.endOrPoint)) case Typed(Ident(name), tpe) if tpe.isType => // get the ident! removeAsPlaceholder(name) - makeParam(name, tpe) + makeParam(name.toTermName, tpe) case _ => syntaxError(tree.pos, "not a legal formal parameter", false) makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.endOrPoint)) @@ -783,7 +783,7 @@ self => val rPos = top.pos val end = if (rPos.isDefined) rPos.endOrPoint else opPos.endOrPoint top = atPos(start, opinfo.offset, end) { - makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos) + makeBinop(isExpr, opinfo.operand, opinfo.operator.toTermName, top, opPos) } } top @@ -1306,7 +1306,7 @@ self => val cond = condExpr() newLinesOpt() val body = expr() - makeWhile(lname, cond, body) + makeWhile(lname.toTermName, cond, body) } } parseWhile @@ -1318,7 +1318,7 @@ self => if (isStatSep) in.nextToken() accept(WHILE) val cond = condExpr() - makeDoWhile(lname, body, cond) + makeDoWhile(lname.toTermName, body, cond) } } parseDo @@ -1477,7 +1477,7 @@ self => def prefixExpr(): Tree = { if (isUnaryOp) { atPos(in.offset) { - val name = nme.toUnaryName(rawIdent()) + val name = nme.toUnaryName(rawIdent().toTermName) if (name == nme.UNARY_- && isNumericLit) simpleExprRest(atPos(in.offset)(literal(isNegated = true)), canApply = true) else @@ -1515,7 +1515,7 @@ self => val pname = freshName("x$") in.nextToken() val id = atPos(start) (Ident(pname)) - val param = atPos(id.pos.focus){ makeSyntheticParam(pname) } + val param = atPos(id.pos.focus){ makeSyntheticParam(pname.toTermName) } placeholderParams = param :: placeholderParams id case LPAREN => @@ -1528,9 +1528,9 @@ self => val nstart = in.skipToken() val npos = r2p(nstart, nstart, in.lastOffset) val tstart = in.offset - val (parents, argss, self, stats) = template(isTrait = false) + val (parents, self, stats) = template() val cpos = r2p(tstart, tstart, in.lastOffset max tstart) - makeNew(parents, self, stats, argss, npos, cpos) + makeNew(parents, self, stats, npos, cpos) case _ => syntaxErrorOrIncomplete("illegal start of simple expression", true) errorTermTree @@ -1898,7 +1898,7 @@ self => case _ => } val typeAppliedTree = in.token match { - case LBRACKET => atPos(start, in.offset)(TypeApply(convertToTypeId(t), typeArgs())) + case LBRACKET => atPos(start, in.offset)(AppliedTypeTree(convertToTypeId(t), typeArgs())) case _ => t } in.token match { @@ -2070,7 +2070,7 @@ self => def annotationExpr(): Tree = atPos(in.offset) { val t = exprSimpleType() if (in.token == LPAREN) New(t, multipleArgumentExprs()) - else New(t, ListOfNil) + else New(t, Nil) } /* -------- PARAMETERS ------------------------------------------- */ @@ -2136,7 +2136,7 @@ self => expr() } else EmptyTree atPos(start, if (name == nme.ERROR) start else nameOffset) { - ValDef((mods | implicitmod | bynamemod) withAnnotations annots, name, tpt, default) + ValDef((mods | implicitmod | bynamemod) withAnnotations annots, name.toTermName, tpt, default) } } def paramClause(): List[ValDef] = { @@ -2696,7 +2696,7 @@ self => atPos(start, if (name == nme.ERROR) start else nameOffset) { val mods1 = if (in.token == SUBTYPE) mods | Flags.DEFERRED else mods val template = templateOpt(mods1, name, NoMods, Nil, tstart) - ModuleDef(mods1, name, template) + ModuleDef(mods1, name.toTermName, template) } } @@ -2705,20 +2705,17 @@ self => * TraitParents ::= AnnotType {with AnnotType} * }}} */ - def templateParents(isTrait: Boolean): (List[Tree], List[List[Tree]]) = { - val parents = new ListBuffer[Tree] += startAnnotType() - val argss = ( - // TODO: the insertion of ListOfNil here is where "new Foo" becomes - // indistinguishable from "new Foo()". - if (in.token == LPAREN && !isTrait) multipleArgumentExprs() - else ListOfNil - ) - - while (in.token == WITH) { - in.nextToken() - parents += startAnnotType() + def templateParents(): List[Tree] = { + val parents = new ListBuffer[Tree] + def readAppliedParent() = { + val start = in.offset + val parent = startAnnotType() + val argss = if (in.token == LPAREN) multipleArgumentExprs() else Nil + parents += atPos(start)((parent /: argss)(Apply.apply)) } - (parents.toList, argss) + readAppliedParent() + while (in.token == WITH) { in.nextToken(); readAppliedParent() } + parents.toList } /** {{{ @@ -2728,12 +2725,12 @@ self => * EarlyDef ::= Annotations Modifiers PatDef * }}} */ - def template(isTrait: Boolean): (List[Tree], List[List[Tree]], ValDef, List[Tree]) = { + def template(): (List[Tree], ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { // @S: pre template body cannot stub like post body can! val (self, body) = templateBody(isPre = true) - if (in.token == WITH && self.isEmpty) { + if (in.token == WITH && (self eq emptyValDef)) { val earlyDefs: List[Tree] = body flatMap { case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) @@ -2745,16 +2742,16 @@ self => case _ => List() } in.nextToken() - val (parents, argss) = templateParents(isTrait = isTrait) - val (self1, body1) = templateBodyOpt(traitParentSeen = isTrait) - (parents, argss, self1, earlyDefs ::: body1) + val parents = templateParents() + val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false) + (parents, self1, earlyDefs ::: body1) } else { - (List(), ListOfNil, self, body) + (List(), self, body) } } else { - val (parents, argss) = templateParents(isTrait = isTrait) - val (self, body) = templateBodyOpt(traitParentSeen = isTrait) - (parents, argss, self, body) + val parents = templateParents() + val (self, body) = templateBodyOpt(parenMeansSyntaxError = false) + (parents, self, body) } } @@ -2768,15 +2765,15 @@ self => * }}} */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { - val (parents0, argss, self, body) = ( + val (parents0, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() - template(isTrait = mods.isTrait) + template() } else { newLineOptWhenFollowedBy(LBRACE) - val (self, body) = templateBodyOpt(traitParentSeen = false) - (List(), ListOfNil, self, body) + val (self, body) = templateBodyOpt(parenMeansSyntaxError = mods.isTrait || name.isTermName) + (List(), self, body) } ) def anyrefParents() = { @@ -2798,7 +2795,7 @@ self => if (inScalaRootPackage && ScalaValueClassNames.contains(name)) Template(parents0, self, anyvalConstructor :: body) else - Template(anyrefParents, self, constrMods, vparamss, argss, body, o2p(tstart)) + Template(anyrefParents, self, constrMods, vparamss, body, o2p(tstart)) } } @@ -2813,14 +2810,15 @@ self => case (self, Nil) => (self, EmptyTree.asList) case result => result } - def templateBodyOpt(traitParentSeen: Boolean): (ValDef, List[Tree]) = { + def templateBodyOpt(parenMeansSyntaxError: Boolean): (ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { templateBody(isPre = false) } else { - if (in.token == LPAREN) - syntaxError((if (traitParentSeen) "parents of traits" else "traits or objects")+ - " may not have parameters", true) + if (in.token == LPAREN) { + if (parenMeansSyntaxError) syntaxError(s"traits or objects may not have parameters", true) + else abort("unexpected opening parenthesis") + } (emptyValDef, List()) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index d6c499d838..39270719fb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -201,20 +201,26 @@ abstract class TreeBuilder { */ def makeAnonymousNew(stats: List[Tree]): Tree = { val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats - makeNew(Nil, emptyValDef, stats1, ListOfNil, NoPosition, NoPosition) + makeNew(Nil, emptyValDef, stats1, NoPosition, NoPosition) } /** Create positioned tree representing an object creation <new parents { stats } * @param npos the position of the new * @param cpos the position of the anonymous class starting with parents */ - def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], argss: List[List[Tree]], + def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], npos: Position, cpos: Position): Tree = if (parents.isEmpty) - makeNew(List(scalaAnyRefConstr), self, stats, argss, npos, cpos) - else if (parents.tail.isEmpty && stats.isEmpty) - atPos(npos union cpos) { New(parents.head, argss) } - else { + makeNew(List(scalaAnyRefConstr), self, stats, npos, cpos) + else if (parents.tail.isEmpty && stats.isEmpty) { + // `Parsers.template` no longer differentiates tpts and their argss + // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil) + // instead of parents = Ident(C), argss = Nil as before + // this change works great for things that are actually templates + // but in this degenerate case we need to perform postprocessing + val app = treeInfo.dissectApplied(parents.head) + atPos(npos union cpos) { New(app.callee, app.argss) } + } else { val x = tpnme.ANON_CLASS_NAME atPos(npos union cpos) { Block( @@ -222,12 +228,12 @@ abstract class TreeBuilder { atPos(cpos) { ClassDef( Modifiers(FINAL), x, Nil, - Template(parents, self, NoMods, ListOfNil, argss, stats, cpos.focus)) + Template(parents, self, NoMods, ListOfNil, stats, cpos.focus)) }), atPos(npos) { New( Ident(x) setPos npos.focus, - ListOfNil) + Nil) } ) } diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index fd4366baf1..5cc4404ca1 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -42,13 +42,9 @@ trait JavaPlatform extends Platform { if (settings.make.isDefault) Nil else List(dependencyAnalysis) - private def classEmitPhase = - if (settings.target.value == "jvm-1.5-fjbg") genJVM - else genASM - def platformPhases = List( flatten, // get rid of inner classes - classEmitPhase // generate .class files + genASM // generate .class files ) ++ depAnalysisPhase lazy val externalEquals = getDecl(BoxesRunTimeClass, nme.equals_) diff --git a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala deleted file mode 100644 index 4493685b52..0000000000 --- a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package backend - -import ch.epfl.lamp.compiler.{ msil => msillib } -import util.{ ClassPath, MsilClassPath } -import msil.GenMSIL -import io.{ AbstractFile, MsilFile } - -trait MSILPlatform extends Platform { - import global._ - import definitions.{ ComparatorClass, BoxedNumberClass, getMember } - - type BinaryRepr = MsilFile - - if (settings.verbose.value) - inform("[AssemRefs = " + settings.assemrefs.value + "]") - - // phaseName = "msil" - object genMSIL extends { - val global: MSILPlatform.this.global.type = MSILPlatform.this.global - val runsAfter = List[String]("dce") - val runsRightAfter = None - } with GenMSIL - - lazy val classPath = MsilClassPath.fromSettings(settings) - def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) - // See discussion in JavaPlatForm for why we need a cast here. - - /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath[BinaryRepr], ClassPath[BinaryRepr]]) = - throw new UnsupportedOperationException("classpath invalidations not supported on MSIL") - - def platformPhases = List( - genMSIL // generate .msil files - ) - - lazy val externalEquals = getMember(ComparatorClass.companionModule, nme.equals_) - def isMaybeBoxed(sym: Symbol) = sym isNonBottomSubClass BoxedNumberClass - - def newClassLoader(bin: MsilFile): loaders.SymbolLoader = new loaders.MsilFileLoader(bin) - - /** - * Tells whether a class should be loaded and entered into the package - * scope. On .NET, this method returns `false` for all synthetic classes - * (anonymous classes, implementation classes, module classes), their - * symtab is encoded in the pickle of another class. - */ - def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = { - if (cls.binary.isDefined) { - val typ = cls.binary.get.msilType - if (typ.IsDefined(loaders.clrTypes.SCALA_SYMTAB_ATTR, false)) { - val attrs = typ.GetCustomAttributes(loaders.clrTypes.SCALA_SYMTAB_ATTR, false) - assert(attrs.length == 1, attrs.length) - val a = attrs(0).asInstanceOf[msillib.Attribute] - // symtab_constr takes a byte array argument (the pickle), i.e. typ has a pickle. - // otherwise, symtab_default_constr was used, which marks typ as scala-synthetic. - a.getConstructor() == loaders.clrTypes.SYMTAB_CONSTR - } else true // always load non-scala types - } else true // always load source - } - - def needCompile(bin: MsilFile, src: AbstractFile) = - false // always use compiled file on .net -} diff --git a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala index 7c2961778f..a872e9cd00 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala @@ -27,9 +27,6 @@ trait ExceptionHandlers { private var _startBlock: BasicBlock = _; var finalizer: Finalizer = _; - /** Needed for the MSIL backend. */ - var resultKind: TypeKind = _; - def setStartBlock(b: BasicBlock) = { _startBlock = b; b.exceptionHandlerStart = true diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index f07c331fb0..cc3562079e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -164,10 +164,7 @@ abstract class GenICode extends SubComponent { private def genStat(tree: Tree, ctx: Context): Context = tree match { case Assign(lhs @ Select(_, _), rhs) => val isStatic = lhs.symbol.isStaticMember - var ctx1 = if (isStatic) ctx - else if (forMSIL && msil_IsValuetypeInstField(lhs.symbol)) - msil_genLoadQualifierAddress(lhs, ctx) - else genLoadQualifier(lhs, ctx) + var ctx1 = if (isStatic) ctx else genLoadQualifier(lhs, ctx) ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info)) ctx1.bb.emit(STORE_FIELD(lhs.symbol, isStatic), tree.pos) @@ -301,9 +298,6 @@ abstract class GenICode extends SubComponent { val Apply(fun, args) = tree val monitor = ctx.makeLocal(tree.pos, ObjectClass.tpe, "monitor") var monitorResult: Local = null - - // if the synchronized block returns a result, store it in a local variable. just leaving - // it on the stack is not valid in MSIL (stack is cleaned when leaving try-blocks) val argTpe = args.head.tpe val hasResult = expectedType != UNIT if (hasResult) @@ -462,131 +456,6 @@ abstract class GenICode extends SubComponent { } /** - * forMSIL - */ - private def msil_IsValuetypeInstMethod(msym: Symbol) = ( - loaders.clrTypes.methods get msym exists (mMSIL => - mMSIL.IsInstance && mMSIL.DeclaringType.IsValueType - ) - ) - private def msil_IsValuetypeInstField(fsym: Symbol) = ( - loaders.clrTypes.fields get fsym exists (fMSIL => - !fMSIL.IsStatic && fMSIL.DeclaringType.IsValueType - ) - ) - - /** - * forMSIL: Adds a local var, the emitted code requires one more slot on the stack as on entry - */ - private def msil_genLoadZeroOfNonEnumValuetype(ctx: Context, kind: TypeKind, pos: Position, leaveAddressOnStackInstead: Boolean) { - val REFERENCE(clssym) = kind - assert(loaders.clrTypes.isNonEnumValuetype(clssym), clssym) - val local = ctx.makeLocal(pos, clssym.tpe, "tmp") - ctx.method.addLocal(local) - ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(local), pos) - ctx.bb.emit(CIL_INITOBJ(kind), pos) - val instr = if (leaveAddressOnStackInstead) - CIL_LOAD_LOCAL_ADDRESS(local) - else - LOAD_LOCAL(local) - ctx.bb.emit(instr, pos) - } - - /** - * forMSIL - */ - private def msil_genLoadAddressOf(tree: Tree, ctx: Context, expectedType: TypeKind, butRawValueIsAlsoGoodEnough: Boolean): Context = { - var generatedType = expectedType - var addressTaken = false - debuglog("at line: " + (if (tree.pos.isDefined) tree.pos.line else tree.pos)) - - var resCtx: Context = tree match { - - // emits CIL_LOAD_FIELD_ADDRESS - case Select(qualifier, selector) if (!tree.symbol.isModule) => - addressTaken = true - val sym = tree.symbol - generatedType = toTypeKind(sym.info) - - if (sym.isStaticMember) { - ctx.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, true), tree.pos) - ctx - } else { - val ctx1 = genLoadQualifier(tree, ctx) - ctx1.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, false), tree.pos) - ctx1 - } - - // emits CIL_LOAD_LOCAL_ADDRESS - case Ident(name) if (!tree.symbol.isPackage && !tree.symbol.isModule)=> - addressTaken = true - val sym = tree.symbol - try { - val Some(l) = ctx.method.lookupLocal(sym) - ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(l), tree.pos) - generatedType = l.kind // actually, should be "V&" but the callsite is aware of this - } catch { - case ex: MatchError => - abort("symbol " + sym + " does not exist in " + ctx.method) - } - ctx - - // emits CIL_LOAD_ARRAY_ITEM_ADDRESS - case Apply(fun, args) => - if (isPrimitive(fun.symbol)) { - val sym = tree.symbol - val Select(receiver, _) = fun - val code = scalaPrimitives.getPrimitive(sym, receiver.tpe) - - if (isArrayOp(code)) { - val arrayObj = receiver - val k = toTypeKind(arrayObj.tpe) - val ARRAY(elementType) = k - if (scalaPrimitives.isArrayGet(code)) { - var ctx1 = genLoad(arrayObj, ctx, k) - // load argument on stack - debugassert(args.length == 1, "Too many arguments for array get operation: " + tree) - ctx1 = genLoad(args.head, ctx1, INT) - generatedType = elementType // actually "managed pointer to element type" but the callsite is aware of this - ctx1.bb.emit(CIL_LOAD_ARRAY_ITEM_ADDRESS(elementType), tree.pos) - addressTaken = true - ctx1 - } else null - } else null - } else null - - case This(qual) => - /* TODO: this case handler is a placeholder for the time when Level 2 support for valuetypes is in place, - in particular when invoking other methods on this where this is a valuetype value (boxed or not). - As receiver, a managed pointer is expected, and a plain ldarg.0 achieves just that. */ - addressTaken = true - genLoad(tree, ctx, expectedType) - - case _ => - null /* A method returning ByRef won't pass peverify, so I guess this case handler is dead code. - Even if it's not, the code below to handler !addressTaken below. */ - } - - if (!addressTaken) { - resCtx = genLoad(tree, ctx, expectedType) - if (!butRawValueIsAlsoGoodEnough) { - // raw value on stack (must be an intermediate result, e.g. returned by method call), take address - addressTaken = true - val boxType = expectedType // toTypeKind(expectedType /* TODO FIXME */) - resCtx.bb.emit(BOX(boxType), tree.pos) - resCtx.bb.emit(CIL_UNBOX(boxType), tree.pos) - } - } - - // emit conversion - if (generatedType != expectedType) - abort("Unexpected tree in msil_genLoadAddressOf: " + tree + " at: " + tree.pos) - - resCtx - } - - - /** * Generate code for trees that produce values on the stack * * @param tree The tree to be translated @@ -743,7 +612,8 @@ abstract class GenICode extends SubComponent { } else ctx1.bb.emit(CONSTANT(Constant(false))) } else if (r.isValueType && cast) { - assert(false, tree) /* Erasure should have added an unboxing operation to prevent that. */ + /* Erasure should have added an unboxing operation to prevent that. */ + abort("should have been unboxed by erasure: " + tree) } else if (r.isValueType) { ctx.bb.emit(IS_INSTANCE(REFERENCE(definitions.boxedClass(r.toType.typeSymbol)))) } else { @@ -808,31 +678,15 @@ abstract class GenICode extends SubComponent { debugassert(ctor.owner == cls, "Symbol " + ctor.owner.fullName + " is different than " + tpt) - val ctx2 = if (forMSIL && loaders.clrTypes.isNonEnumValuetype(cls)) { - /* parameterful constructors are the only possible custom constructors, - a default constructor can't be defined for valuetypes, CLR dixit */ - val isDefaultConstructor = args.isEmpty - if (isDefaultConstructor) { - msil_genLoadZeroOfNonEnumValuetype(ctx, rt, tree.pos, leaveAddressOnStackInstead = false) - ctx - } else { - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - ctx1.bb.emit(CIL_NEWOBJ(ctor), tree.pos) - ctx1 - } - } else { - val nw = NEW(rt) - ctx.bb.emit(nw, tree.pos) - ctx.bb.emit(DUP(generatedType)) - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - - val init = CALL_METHOD(ctor, Static(true)) - nw.init = init - ctx1.bb.emit(init, tree.pos) - ctx1 - } - ctx2 + val nw = NEW(rt) + ctx.bb.emit(nw, tree.pos) + ctx.bb.emit(DUP(generatedType)) + val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) + val init = CALL_METHOD(ctor, Static(true)) + nw.init = init + ctx1.bb.emit(init, tree.pos) + ctx1 case _ => abort("Cannot instantiate " + tpt + " of kind: " + generatedType) } @@ -866,12 +720,6 @@ abstract class GenICode extends SubComponent { ctx1.bb.emit(UNBOX(boxType), expr.pos) ctx1 - case Apply(fun @ _, List(expr)) if (forMSIL && loaders.clrTypes.isAddressOf(fun.symbol)) => - debuglog("ADDRESSOF : " + fun.symbol.fullName); - val ctx1 = msil_genLoadAddressOf(expr, ctx, toTypeKind(expr.tpe), butRawValueIsAlsoGoodEnough = false) - generatedType = toTypeKind(fun.symbol.tpe.resultType) - ctx1 - case app @ Apply(fun, args) => def genLoadApply6 = { val sym = fun.symbol @@ -913,19 +761,12 @@ abstract class GenICode extends SubComponent { else Dynamic - var ctx1 = - if (invokeStyle.hasInstance) { - if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym)) - msil_genLoadQualifierAddress(fun, ctx) - else - genLoadQualifier(fun, ctx) - } else ctx - + var ctx1 = if (invokeStyle.hasInstance) genLoadQualifier(fun, ctx) else ctx ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1) val cm = CALL_METHOD(sym, invokeStyle) /** In a couple cases, squirrel away a little extra information in the - * CALL_METHOD for use by GenJVM. + * CALL_METHOD for use by GenASM. */ fun match { case Select(qual, _) => @@ -956,7 +797,6 @@ abstract class GenICode extends SubComponent { genLoadApply6 case ApplyDynamic(qual, args) => - assert(!forMSIL, tree) // TODO - this is where we'd catch dynamic applies for invokedynamic. sys.error("No invokedynamic support yet.") // val ctx1 = genLoad(qual, ctx, ObjectReference) @@ -1189,15 +1029,6 @@ abstract class GenICode extends SubComponent { abort("Unknown qualifier " + tree) } - /** forMSIL */ - private def msil_genLoadQualifierAddress(tree: Tree, ctx: Context): Context = - tree match { - case Select(qualifier, _) => - msil_genLoadAddressOf(qualifier, ctx, toTypeKind(qualifier.tpe), butRawValueIsAlsoGoodEnough = false) - case _ => - abort("Unknown qualifier " + tree) - } - /** * Generate code that loads args into label parameters. */ @@ -1243,8 +1074,7 @@ abstract class GenICode extends SubComponent { val sym = ( if (!tree.symbol.isPackageClass) tree.symbol else tree.symbol.info.member(nme.PACKAGE) match { - case NoSymbol => - abort("Cannot use package as value: " + tree) + case NoSymbol => abort("Cannot use package as value: " + tree) case s => devWarning(s"Found ${tree.symbol} where a package object is required. Converting to ${s.moduleClass}") s.moduleClass @@ -1385,7 +1215,7 @@ abstract class GenICode extends SubComponent { def genStringConcat(tree: Tree, ctx: Context): Context = { liftStringConcat(tree) match { // Optimization for expressions of the form "" + x. We can avoid the StringBuilder. - case List(Literal(Constant("")), arg) if !forMSIL => + case List(Literal(Constant("")), arg) => debuglog("Rewriting \"\" + x as String.valueOf(x) for: " + arg) val ctx1 = genLoad(arg, ctx, ObjectReference) ctx1.bb.emit(CALL_METHOD(String_valueOf, Static(false)), arg.pos) @@ -1565,7 +1395,7 @@ abstract class GenICode extends SubComponent { */ def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context)(thenCtx: Context, elseCtx: Context): Unit = { def getTempLocal = ctx.method.lookupLocal(nme.EQEQ_LOCAL_VAR) getOrElse { - ctx.makeLocal(l.pos, AnyRefClass.tpe, nme.EQEQ_LOCAL_VAR) + ctx.makeLocal(l.pos, AnyRefClass.tpe, nme.EQEQ_LOCAL_VAR.toString) } /** True if the equality comparison is between values that require the use of the rich equality @@ -1988,10 +1818,9 @@ abstract class GenICode extends SubComponent { * 'covered' by this exception handler (in addition to the * previously active handlers). */ - private def newExceptionHandler(cls: Symbol, resultKind: TypeKind, pos: Position): ExceptionHandler = { + private def newExceptionHandler(cls: Symbol, pos: Position): ExceptionHandler = { handlerCount += 1 val exh = new ExceptionHandler(method, newTermNameCached("" + handlerCount), cls, pos) - exh.resultKind = resultKind method.addHandler(exh) handlers = exh :: handlers debuglog("added handler: " + exh); @@ -2051,7 +1880,7 @@ abstract class GenICode extends SubComponent { def Try(body: Context => Context, handlers: List[(Symbol, TypeKind, Context => Context)], finalizer: Tree, - tree: Tree) = if (forMSIL) TryMsil(body, handlers, finalizer, tree) else { + tree: Tree) = { val outerCtx = this.dup // context for generating exception handlers, covered by finalizer val finalizerCtx = this.dup // context for generating finalizer handler @@ -2083,7 +1912,7 @@ abstract class GenICode extends SubComponent { if (finalizer != EmptyTree) { - val exh = outerCtx.newExceptionHandler(NoSymbol, toTypeKind(finalizer.tpe), finalizer.pos) // finalizer covers exception handlers + val exh = outerCtx.newExceptionHandler(NoSymbol, finalizer.pos) // finalizer covers exception handlers this.addActiveHandler(exh) // .. and body aswell val ctx = finalizerCtx.enterExceptionHandler(exh) val exception = ctx.makeLocal(finalizer.pos, ThrowableClass.tpe, "exc") @@ -2098,7 +1927,7 @@ abstract class GenICode extends SubComponent { } for ((sym, kind, handler) <- handlers) { - val exh = this.newExceptionHandler(sym, kind, tree.pos) + val exh = this.newExceptionHandler(sym, tree.pos) var ctx1 = outerCtx.enterExceptionHandler(exh) ctx1.addFinalizer(finalizer, finalizerCtx) loadException(ctx1, exh, tree.pos) @@ -2122,67 +1951,6 @@ abstract class GenICode extends SubComponent { afterCtx } - - - /** try-catch-finally blocks are actually simpler to emit in MSIL, because there - * is support for `finally` in bytecode. - * - * A - * try { .. } catch { .. } finally { .. } - * block is de-sugared into - * try { try { ..} catch { .. } } finally { .. } - * - * In ICode `finally` block is represented exactly the same as an exception handler, - * but with `NoSymbol` as the exception class. The covered blocks are all blocks of - * the `try { .. } catch { .. }`. - * - * Also, TryMsil does not enter any Finalizers into the `cleanups`, because the - * CLI takes care of running the finalizer when seeing a `leave` statement inside - * a try / catch. - */ - def TryMsil(body: Context => Context, - handlers: List[(Symbol, TypeKind, (Context => Context))], - finalizer: Tree, - tree: Tree) = { - - val outerCtx = this.dup // context for generating exception handlers, covered by finalizer - val finalizerCtx = this.dup // context for generating finalizer handler - val afterCtx = outerCtx.newBlock - - if (finalizer != EmptyTree) { - // finalizer is covers try and all catch blocks, i.e. - // try { try { .. } catch { ..} } finally { .. } - val exh = outerCtx.newExceptionHandler(NoSymbol, UNIT, tree.pos) - this.addActiveHandler(exh) - val ctx = finalizerCtx.enterExceptionHandler(exh) - loadException(ctx, exh, tree.pos) - val ctx1 = genLoad(finalizer, ctx, UNIT) - // need jump for the ICode to be valid. MSIL backend will emit `Endfinally` instead. - ctx1.bb.closeWith(JUMP(afterCtx.bb)) - finalizerCtx.endHandler() - } - - for (handler <- handlers) { - val exh = this.newExceptionHandler(handler._1, handler._2, tree.pos) - var ctx1 = outerCtx.enterExceptionHandler(exh) - loadException(ctx1, exh, tree.pos) - ctx1 = handler._3(ctx1) - // msil backend will emit `Leave` to jump out of a handler - ctx1.bb.closeWith(JUMP(afterCtx.bb)) - outerCtx.endHandler() - } - - val bodyCtx = this.newBlock - - val finalCtx = body(bodyCtx) - - outerCtx.bb.closeWith(JUMP(bodyCtx.bb)) - - // msil backend will emit `Leave` to jump out of a try-block - finalCtx.bb.closeWith(JUMP(afterCtx.bb)) - - afterCtx - } } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala index b8a98955c9..80477f0c6e 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala @@ -197,142 +197,4 @@ trait Linearizers { def linearize(m: IMethod): List[BasicBlock] = m.blocks def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = sys.error("not implemented") } - - /** The MSIL linearizer is used only for methods with at least one exception handler. - * It makes sure that all the blocks belonging to a `try`, `catch` or `finally` block - * are emitted in an order that allows the lexical nesting of try-catch-finally, just - * like in the source code. - */ - class MSILLinearizer extends Linearizer { - /** The MSIL linearizer first calls a NormalLInearizer. This is because the ILGenerator checks - * the stack size before emitting instructions. For instance, to emit a `store`, there needs - * to be some value on the stack. This can blow up in situations like this: - * ... - * jump 3 - * 4: store_local 0 - * jump 5 - * 3: load_value - * jump 4 - * 5: ... - * here, 3 must be scheduled first. - * - * The NormalLinearizer also removes dead blocks (blocks without predecessor). This is important - * in the following example: - * try { throw new Exception } - * catch { case e => throw e } - * which adds a dead block containing just a "throw" (which, again, would blow up code generation - * because of the stack size; there's no value on the stack when emitting that `throw`) - */ - val normalLinearizer = new NormalLinearizer() - - def linearize(m: IMethod): List[BasicBlock] = { - - val handlersByCovered = m.exh.groupBy(_.covered) - - // number of basic blocks covered by the entire try-catch expression - def size(covered: scala.collection.immutable.Set[BasicBlock]) = { - val hs = handlersByCovered(covered) - covered.size + (hs :\ 0)((h, s) => h.blocks.length + s) - } - - val tryBlocks = handlersByCovered.keys.toList sortBy size - var result = normalLinearizer.linearize(m) - val frozen = mutable.HashSet[BasicBlock](result.head) - - for (tryBlock <- tryBlocks) { - result = groupBlocks(m, result, handlersByCovered(tryBlock), frozen) - } - result - } - - /** @param handlers a list of handlers covering the same blocks (same try, multiple catches) - * @param frozen blocks can't be moved (fist block of a method, blocks directly following a try-catch) - */ - def groupBlocks(method: IMethod, blocks: List[BasicBlock], handlers: List[ExceptionHandler], frozen: mutable.HashSet[BasicBlock]) = { - assert(blocks.head == method.startBlock, method) - - // blocks before the try, and blocks for the try - val beforeAndTry = new ListBuffer[BasicBlock]() - // blocks for the handlers - val catches = handlers map (_ => new ListBuffer[BasicBlock]()) - // blocks to be put at the end - val after = new ListBuffer[BasicBlock]() - - var beforeTry = true - val head = handlers.head - - for (b <- blocks) { - if (head covers b) { - beforeTry = false - beforeAndTry += b - } else { - val handlerIndex = handlers.indexWhere(_.blocks.contains(b)) - if (handlerIndex >= 0) { - catches(handlerIndex) += b - } else if (beforeTry) { - beforeAndTry += b - } else { - after += b - } - } - } - - // reorder the blocks in "catches" so that the "firstBlock" is actually first - (catches, handlers).zipped foreach { (lb, handler) => - lb -= handler.startBlock - handler.startBlock +=: lb - } - - // The first block emitted after a try-catch must be the one that the try / catch - // blocks jump to (because in msil, these jumps cannot be emitted manually) - var firstAfter: Option[BasicBlock] = None - - // Find the (hopefully) unique successor, look at the try and all catch blocks - var blks = head.covered.toList :: handlers.map(_.blocks) - while (firstAfter.isEmpty && !blks.isEmpty) { - val b = blks.head - blks = blks.tail - - val leaving = leavingBlocks(b) - // no leaving blocks when the try or catch ends with THROW or RET - if (!leaving.isEmpty) { - assert(leaving.size <= 1, leaving) - firstAfter = Some(leaving.head) - } - } - if (firstAfter.isDefined) { - val b = firstAfter.get - if (frozen(b)) { - assert(after contains b, b +", "+ method) - } else { - frozen += b - if (beforeAndTry contains b) { - beforeAndTry -= b - } else { - assert(after contains b, after) - after -= b - } - b +=: after - } - } - - for (lb <- catches) { beforeAndTry ++= lb } - beforeAndTry ++= after - beforeAndTry.toList - } - - /** Returns all direct successors of `blocks` wich are not part - * that list, i.e. successors outside the `blocks` list. - */ - private def leavingBlocks(blocks: List[BasicBlock]) = { - val res = new mutable.HashSet[BasicBlock]() - for (b <- blocks; s <- b.directSuccessors; if (!blocks.contains(s))) - res += s - res - } - - def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = { - sys.error("not implemented") - } - } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index eaa742a1da..137e2b556f 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -734,74 +734,5 @@ trait Opcodes { self: ICodes => case class SuperCall(mix: Name) extends InvokeStyle { override def toString(): String = { "super(" + mix + ")" } } - - - // CLR backend - - case class CIL_LOAD_LOCAL_ADDRESS(local: Local) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = "CIL_LOAD_LOCAL_ADDRESS "+local //+isArgument?" (argument)":""; - - override def consumed = 0 - override def produced = 1 - - override def producedTypes = msil_mgdptr(local.kind) :: Nil - - override def category = localsCat - } - - case class CIL_LOAD_FIELD_ADDRESS(field: Symbol, isStatic: Boolean) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = - "CIL_LOAD_FIELD_ADDRESS " + (if (isStatic) field.fullName else field.toString) - - override def consumed = if (isStatic) 0 else 1 - override def produced = 1 - - override def consumedTypes = if (isStatic) Nil else REFERENCE(field.owner) :: Nil; - override def producedTypes = msil_mgdptr(REFERENCE(field.owner)) :: Nil; - - override def category = fldsCat - } - - case class CIL_LOAD_ARRAY_ITEM_ADDRESS(kind: TypeKind) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = "CIL_LOAD_ARRAY_ITEM_ADDRESS (" + kind + ")" - - override def consumed = 2 - override def produced = 1 - - override def consumedTypes = ARRAY(kind) :: INT :: Nil - override def producedTypes = msil_mgdptr(kind) :: Nil - - override def category = arraysCat - } - - case class CIL_UNBOX(valueType: TypeKind) extends Instruction { - override def toString(): String = "CIL_UNBOX " + valueType - override def consumed = 1 - override def consumedTypes = ObjectReferenceList // actually consumes a 'boxed valueType' - override def produced = 1 - override def producedTypes = msil_mgdptr(valueType) :: Nil - override def category = objsCat - } - - case class CIL_INITOBJ(valueType: TypeKind) extends Instruction { - override def toString(): String = "CIL_INITOBJ " + valueType - override def consumed = 1 - override def consumedTypes = ObjectReferenceList // actually consumes a managed pointer - override def produced = 0 - override def category = objsCat - } - - case class CIL_NEWOBJ(method: Symbol) extends Instruction { - override def toString(): String = "CIL_NEWOBJ " + hostClass.fullName + method.fullName - val hostClass: Symbol = method.owner; - override def consumed = method.tpe.paramTypes.length - override def consumedTypes = method.tpe.paramTypes map toTypeKind - override def produced = 1 - override def producedTypes = toTypeKind(method.tpe.resultType) :: Nil - override def category = objsCat - } } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 0990cfba6f..84f5fe2678 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -427,11 +427,4 @@ trait TypeKinds { self: ICodes => primitiveTypeMap.getOrElse(sym, newReference(sym)) private def primitiveOrClassType(sym: Symbol, targs: List[Type]) = primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs)) - - def msil_mgdptr(tk: TypeKind): TypeKind = (tk: @unchecked) match { - case REFERENCE(cls) => REFERENCE(loaders.clrTypes.mdgptrcls4clssym(cls)) - // TODO have ready class-symbols for the by-ref versions of built-in valuetypes - case _ => abort("cannot obtain a managed pointer for " + tk) - } - } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 8c8950d295..941ccd9a2d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -9,7 +9,7 @@ package backend.jvm import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile } import scala.tools.nsc.io._ import scala.tools.nsc.util.ScalaClassLoader -import scala.tools.util.JavapClass +import scala.tools.util.{ Javap, JavapClass } import java.util.jar.Attributes.Name import scala.language.postfixOps @@ -59,27 +59,32 @@ trait BytecodeWriters { override def close() = writer.close() } + /** To be mixed-in with the BytecodeWriter that generates + * the class file to be disassembled. + */ trait JavapBytecodeWriter extends BytecodeWriter { val baseDir = Directory(settings.Ygenjavap.value).createDirectory() - - def emitJavap(bytes: Array[Byte], javapFile: io.File) { - val pw = javapFile.printWriter() - val javap = new JavapClass(ScalaClassLoader.appLoader, pw) { - override def findBytes(path: String): Array[Byte] = bytes - } - - try javap(Seq("-verbose", "dummy")) foreach (_.show()) - finally pw.close() + val cl = ScalaClassLoader.appLoader + + def emitJavap(classFile: AbstractFile, javapFile: File) { + val pw = javapFile.printWriter() + try { + val javap = new JavapClass(cl, pw) { + override def findBytes(path: String): Array[Byte] = classFile.toByteArray + } + javap(Seq("-verbose", "-protected", classFile.name)) foreach (_.show()) + } finally pw.close() } abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { super.writeClass(label, jclassName, jclassBytes, sym) - val bytes = getFile(sym, jclassName, ".class").toByteArray + val classFile = getFile(sym, jclassName, ".class") val segments = jclassName.split("[./]") val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile; - javapFile.parent.createDirectory() - emitJavap(bytes, javapFile) + + if (Javap.isAvailable(cl)) emitJavap(classFile, javapFile) + else warning("No javap on classpath, skipping javap output.") } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 1aa80d5c5b..92d732ed04 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -18,7 +18,7 @@ import asm.Label * * Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf */ -abstract class GenASM extends SubComponent with BytecodeWriters { +abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { import global._ import icodes._ import icodes.opcodes._ @@ -50,61 +50,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // cause cycles before Global has finished initialization. lazy val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") - def isJavaEntryPoint(icls: IClass) = { - val sym = icls.symbol - def fail(msg: String, pos: Position = sym.pos) = { - icls.cunit.warning(sym.pos, - sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + - " Reason: " + msg - // TODO: make this next claim true, if possible - // by generating valid main methods as static in module classes - // not sure what the jvm allows here - // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." - ) - false - } - def failNoForwarder(msg: String) = { - fail(msg + ", which means no static forwarder can be generated.\n") - } - val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil - val hasApproximate = possibles exists { m => - m.info match { - case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass - case _ => false - } - } - // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. - hasApproximate && { - // Before erasure so we can identify generic mains. - enteringErasure { - val companion = sym.linkedClassOfClass - - if (hasJavaMainMethod(companion)) - failNoForwarder("companion contains its own main method") - else if (companion.tpe.member(nme.main) != NoSymbol) - // this is only because forwarders aren't smart enough yet - failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") - else if (companion.isTrait) - failNoForwarder("companion is a trait") - // Now either succeeed, or issue some additional warnings for things which look like - // attempts to be java main methods. - else possibles exists { m => - m.info match { - case PolyType(_, _) => - fail("main methods cannot be generic.") - case MethodType(params, res) => - if (res.typeSymbol :: params exists (_.isAbstractType)) - fail("main methods cannot refer to type parameters or abstract types.", m.pos) - else - isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) - case tp => - fail("don't know what this is: " + tp, m.pos) - } - } - } - } - } - private def initBytecodeWriter(entryPoints: List[IClass]): BytecodeWriter = { settings.outputDirs.getSingleOutput match { case Some(f) if f hasExtension "jar" => @@ -126,13 +71,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters { new DirectToJarfileWriter(f.file) case _ => + import scala.tools.util.Javap if (settings.Ygenjavap.isDefault) { if(settings.Ydumpclasses.isDefault) new ClassBytecodeWriter { } else new ClassBytecodeWriter with DumpBytecodeWriter { } } - else new ClassBytecodeWriter with JavapBytecodeWriter { } + else if (Javap.isAvailable()) new ClassBytecodeWriter with JavapBytecodeWriter { } + else { + warning("No javap on classpath, skipping javap output.") + new ClassBytecodeWriter { } + } // TODO A ScalapBytecodeWriter could take asm.util.Textifier as starting point. // Three areas where javap ouput is less than ideal (e.g. when comparing versions of the same classfile) are: @@ -154,7 +104,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } // For predictably ordered error messages. - var sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName) + var sortedClasses = classes.values.toList sortBy (_.symbol.fullName) + + // Warn when classes will overwrite one another on case-insensitive systems. + for ((_, v1 :: v2 :: _) <- sortedClasses groupBy (_.symbol.javaClassName.toString.toLowerCase)) { + v1.cunit.warning(v1.symbol.pos, + s"Class ${v1.symbol.javaClassName} differs only in case from ${v2.symbol.javaClassName}. " + + "Such classes will overwrite one another on case-insensitive filesystems.") + } debuglog("Created new bytecode generator for " + classes.size + " classes.") val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint) @@ -190,7 +147,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { /* don't javaNameCache.clear() because that causes the following tests to fail: * test/files/run/macro-repl-dontexpand.scala * test/files/jvm/interpreter.scala - * TODO but why? what use could javaNameCache possibly see once GenJVM is over? + * TODO but why? what use could javaNameCache possibly see once GenASM is over? */ /* TODO After emitting all class files (e.g., in a separate compiler phase) ASM can perform bytecode verification: @@ -888,10 +845,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // Run the signature parser to catch bogus signatures. val isValidSignature = wrap { // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.SignatureChecker - if (sym.isMethod) { SignatureChecker checkMethodSignature sig } // requires asm-util.jar - else if (sym.isTerm) { SignatureChecker checkFieldSignature sig } - else { SignatureChecker checkClassSignature sig } + import scala.tools.asm.util.CheckClassAdapter + if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar + else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } + else { CheckClassAdapter checkClassSignature sig } } if(!isValidSignature) { @@ -1088,12 +1045,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // a plain class lacking companion module, for details see `isCandidateForForwarders`). // ----------------------------------------------------------------------------------------- - val ExcludedForwarderFlags = { - import Flags._ - // Should include DEFERRED but this breaks findMember. - ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags ) - } - /** Add a forwarder for method m. Used only from addForwarders(). */ private def addForwarder(isRemoteClass: Boolean, jclass: asm.ClassVisitor, module: Symbol, m: Symbol) { val moduleName = javaName(module) @@ -1198,7 +1149,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { /* Typestate: should be called before emitting fields (because it adds an IField to the current IClass). */ def addCreatorCode(block: BasicBlock) { val fieldSymbol = ( - clasz.symbol.newValue(newTermName(androidFieldName), NoPosition, Flags.STATIC | Flags.FINAL) + clasz.symbol.newValue(androidFieldName, NoPosition, Flags.STATIC | Flags.FINAL) setInfo AndroidCreatorClass.tpe ) val methodSymbol = definitions.getMember(clasz.symbol.companionModule, androidFieldName) @@ -1213,7 +1164,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { jclass.visitField( PublicStaticFinal, - androidFieldName, + androidFieldName.toString, tdesc_creator, null, // no java-generic-signature null // no initial value @@ -1233,7 +1184,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { clinit.visitMethodInsn( asm.Opcodes.INVOKEVIRTUAL, moduleName, - androidFieldName, + androidFieldName.toString, asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*) ) @@ -1241,7 +1192,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { clinit.visitFieldInsn( asm.Opcodes.PUTSTATIC, thisName, - androidFieldName, + androidFieldName.toString, tdesc_creator ) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenAndroid.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenAndroid.scala deleted file mode 100644 index 72b7e35408..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenAndroid.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Stephane Micheloud - */ - - -package scala.tools.nsc -package backend.jvm - -import ch.epfl.lamp.fjbg._ -import symtab.Flags - -trait GenAndroid { - self: GenJVM => - - import global._ - import icodes._ - import opcodes._ - - /** From the reference documentation of the Android SDK: - * The `Parcelable` interface identifies classes whose instances can be - * written to and restored from a `Parcel`. Classes implementing the - * `Parcelable` interface must also have a static field called `CREATOR`, - * which is an object implementing the `Parcelable.Creator` interface. - */ - private val fieldName = newTermName("CREATOR") - - private lazy val AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable") - private lazy val AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator") - - def isAndroidParcelableClass(sym: Symbol) = - (AndroidParcelableInterface != NoSymbol) && - (sym.parentSymbols contains AndroidParcelableInterface) - - def addCreatorCode(codegen: BytecodeGenerator, block: BasicBlock) { - import codegen._ - val fieldSymbol = ( - clasz.symbol.newValue(newTermName(fieldName), NoPosition, Flags.STATIC | Flags.FINAL) - setInfo AndroidCreatorClass.tpe - ) - val methodSymbol = definitions.getMember(clasz.symbol.companionModule, fieldName) - clasz addField new IField(fieldSymbol) - block emit CALL_METHOD(methodSymbol, Static(false)) - block emit STORE_FIELD(fieldSymbol, true) - } - - def legacyAddCreatorCode(codegen: BytecodeGenerator, clinit: JExtendedCode) { - import codegen._ - val creatorType = javaType(AndroidCreatorClass) - jclass.addNewField(PublicStaticFinal, - fieldName, - creatorType) - val moduleName = javaName(clasz.symbol)+"$" - clinit.emitGETSTATIC(moduleName, - nme.MODULE_INSTANCE_FIELD.toString, - new JObjectType(moduleName)) - clinit.emitINVOKEVIRTUAL(moduleName, fieldName, - new JMethodType(creatorType, Array())) - clinit.emitPUTSTATIC(jclass.getName(), fieldName, creatorType) - } - -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala deleted file mode 100644 index e1484d1f97..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ /dev/null @@ -1,1950 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Iulian Dragos - */ - -package scala.tools.nsc -package backend.jvm - -import java.nio.ByteBuffer -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.pickling.{ PickleFormat, PickleBuffer } -import scala.tools.nsc.symtab._ -import scala.reflect.internal.util.{ SourceFile, NoSourceFile } -import scala.reflect.internal.ClassfileConstants._ -import ch.epfl.lamp.fjbg._ -import JAccessFlags._ -import JObjectType.{ JAVA_LANG_STRING, JAVA_LANG_OBJECT } -import scala.language.postfixOps - -/** This class ... - * - * @author Iulian Dragos - * @version 1.0 - * - */ -abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with BytecodeWriters { - import global._ - import icodes._ - import icodes.opcodes._ - import definitions._ - - val phaseName = "jvm" - - /** Create a new phase */ - override def newPhase(p: Phase): Phase = new JvmPhase(p) - - /** JVM code generation phase - */ - class JvmPhase(prev: Phase) extends ICodePhase(prev) { - def name = phaseName - override def erasedTypes = true - def apply(cls: IClass) = sys.error("no implementation") - - def isJavaEntryPoint(clasz: IClass) = { - val sym = clasz.symbol - def fail(msg: String, pos: Position = sym.pos) = { - clasz.cunit.warning(sym.pos, - sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + - " Reason: " + msg - // TODO: make this next claim true, if possible - // by generating valid main methods as static in module classes - // not sure what the jvm allows here - // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." - ) - false - } - def failNoForwarder(msg: String) = { - fail(msg + ", which means no static forwarder can be generated.\n") - } - val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil - val hasApproximate = possibles exists { m => - m.info match { - case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass - case _ => false - } - } - // At this point it's a module with a main-looking method, so either - // succeed or warn that it isn't. - hasApproximate && { - // Before erasure so we can identify generic mains. - enteringErasure { - val companion = sym.linkedClassOfClass - - if (hasJavaMainMethod(companion)) - failNoForwarder("companion contains its own main method") - else if (companion.tpe.member(nme.main) != NoSymbol) - // this is only because forwarders aren't smart enough yet - failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") - else if (companion.isTrait) - failNoForwarder("companion is a trait") - // Now either succeeed, or issue some additional warnings for things which look like - // attempts to be java main methods. - else possibles exists { m => - m.info match { - case PolyType(_, _) => - fail("main methods cannot be generic.") - case MethodType(params, res) => - if (res.typeSymbol :: params exists (_.isAbstractType)) - fail("main methods cannot refer to type parameters or abstract types.", m.pos) - else - isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) - case tp => - fail("don't know what this is: " + tp, m.pos) - } - } - } - } - } - - override def run() { - // we reinstantiate the bytecode generator at each run, to allow the GC - // to collect everything - if (settings.debug.value) - inform("[running phase " + name + " on icode]") - - if (settings.Xdce.value) - for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) { - log(s"Optimizer eliminated ${sym.fullNameString}") - icodes.classes -= sym - } - - // For predictably ordered error messages. - val sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName) - val entryPoints = sortedClasses filter isJavaEntryPoint - - val bytecodeWriter = settings.outputDirs.getSingleOutput match { - case Some(f) if f hasExtension "jar" => - // If no main class was specified, see if there's only one - // entry point among the classes going into the jar. - if (settings.mainClass.isDefault) { - entryPoints map (_.symbol fullName '.') match { - case Nil => - log("No Main-Class designated or discovered.") - case name :: Nil => - log("Unique entry point: setting Main-Class to " + name) - settings.mainClass.value = name - case names => - log("No Main-Class due to multiple entry points:\n " + names.mkString("\n ")) - } - } - else log("Main-Class was specified: " + settings.mainClass.value) - - new DirectToJarfileWriter(f.file) - - case _ => - if (settings.Ygenjavap.isDefault) { - if(settings.Ydumpclasses.isDefault) - new ClassBytecodeWriter { } - else - new ClassBytecodeWriter with DumpBytecodeWriter { } - } - else new ClassBytecodeWriter with JavapBytecodeWriter { } - } - - val codeGenerator = new BytecodeGenerator(bytecodeWriter) - debuglog("Created new bytecode generator for " + classes.size + " classes.") - - sortedClasses foreach { c => - try codeGenerator.genClass(c) - catch { - case e: JCode.CodeSizeTooBigException => - log("Skipped class %s because it has methods that are too long.".format(c)) - } - } - - bytecodeWriter.close() - classes.clear() - } - } - - var pickledBytes = 0 // statistics - - /** - * Java bytecode generator. - * - */ - class BytecodeGenerator(bytecodeWriter: BytecodeWriter) extends BytecodeUtil { - def this() = this(new ClassBytecodeWriter { }) - def debugLevel = settings.debuginfo.indexOfChoice - import bytecodeWriter.writeClass - - val MIN_SWITCH_DENSITY = 0.7 - val INNER_CLASSES_FLAGS = - (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_INTERFACE | ACC_ABSTRACT) - - val PublicStatic = ACC_PUBLIC | ACC_STATIC - val PublicStaticFinal = ACC_PUBLIC | ACC_STATIC | ACC_FINAL - - val StringBuilderClassName = javaName(definitions.StringBuilderClass) - val BoxesRunTime = "scala.runtime.BoxesRunTime" - - val StringBuilderType = new JObjectType(StringBuilderClassName) // TODO use ASMType.getObjectType - val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY) // TODO use ASMType.getMethodType - val arrayCloneType = new JMethodType(JAVA_LANG_OBJECT, JType.EMPTY_ARRAY) - - // Scala attributes - val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") - - final val ExcludedForwarderFlags = { - import Flags._ - // Should include DEFERRED but this breaks findMember. - ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags ) - } - - // Additional interface parents based on annotations and other cues - def newParentForAttr(attr: Symbol): Option[Symbol] = attr match { - case CloneableAttr => Some(JavaCloneableClass) - case RemoteAttr => Some(RemoteInterfaceClass) - case _ => None - } - - val versionPickle = { - val vp = new PickleBuffer(new Array[Byte](16), -1, 0) - assert(vp.writeIndex == 0, vp) - vp writeNat PickleFormat.MajorVersion - vp writeNat PickleFormat.MinorVersion - vp writeNat 0 - vp - } - - private def helperBoxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = { - val boxedType = definitions.boxedClass(kind.toType.typeSymbol) - val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind))) - - Pair("boxTo" + boxedType.decodedName, mtype) - } - - private val jBoxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map( - BOOL -> helperBoxTo(BOOL) , - BYTE -> helperBoxTo(BYTE) , - CHAR -> helperBoxTo(CHAR) , - SHORT -> helperBoxTo(SHORT) , - INT -> helperBoxTo(INT) , - LONG -> helperBoxTo(LONG) , - FLOAT -> helperBoxTo(FLOAT) , - DOUBLE -> helperBoxTo(DOUBLE) - ) - - private def helperUnboxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = { - val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT)) - val mname = "unboxTo" + kind.toType.typeSymbol.decodedName - - Pair(mname, mtype) - } - - private val jUnboxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map( - BOOL -> helperUnboxTo(BOOL) , - BYTE -> helperUnboxTo(BYTE) , - CHAR -> helperUnboxTo(CHAR) , - SHORT -> helperUnboxTo(SHORT) , - INT -> helperUnboxTo(INT) , - LONG -> helperUnboxTo(LONG) , - FLOAT -> helperUnboxTo(FLOAT) , - DOUBLE -> helperUnboxTo(DOUBLE) - ) - - var clasz: IClass = _ - var method: IMethod = _ - var jclass: JClass = _ - var jmethod: JMethod = _ - - def isParcelableClass = isAndroidParcelableClass(clasz.symbol) - def isRemoteClass = clasz.symbol hasAnnotation RemoteAttr - def serialVUID = clasz.symbol getAnnotation SerialVersionUIDAttr collect { - case AnnotationInfo(_, Literal(const) :: _, _) => const.longValue - } - - val fjbgContext = new FJBGContext(49, 0) - val emitVars = debugLevel >= 3 - - // bug had phase with wrong name; leaving enabled for brief pseudo deprecation - private val checkSignatures = ( - (settings.check containsName phaseName) - || (settings.check.value contains "genjvm") && { - global.warning("This option will be removed: please use -Ycheck:%s, not -Ycheck:genjvm." format phaseName) - true - } - ) - - /** For given symbol return a symbol corresponding to a class that should be declared as inner class. - * - * For example: - * class A { - * class B - * object C - * } - * - * then method will return NoSymbol for A, the same symbol for A.B (corresponding to A$B class) and A$C$ symbol - * for A.C. - */ - private def innerClassSymbolFor(s: Symbol): Symbol = - if (s.isClass) s else if (s.isModule) s.moduleClass else NoSymbol - - override def javaName(sym: Symbol): String = { // TODO Miguel says: check whether a single pass over `icodes.classes` can populate `innerClassBuffer` faster. - /** - * Checks if given symbol corresponds to inner class/object and add it to innerClassBuffer - * - * Note: This method is called recursively thus making sure that we add complete chain - * of inner class all until root class. - */ - def collectInnerClass(s: Symbol): Unit = { - // TODO: some enteringFlatten { ... } which accounts for - // being nested in parameterized classes (if we're going to selectively flatten.) - val x = innerClassSymbolFor(s) - if(x ne NoSymbol) { - assert(x.isClass, "not an inner-class symbol") - val isInner = !x.rawowner.isPackageClass - if (isInner) { - innerClassBuffer += x - collectInnerClass(x.rawowner) - } - } - } - collectInnerClass(sym) - - super.javaName(sym) - } - - /** Write a class to disk, adding the Scala signature (pickled type - * information) and inner classes. - * - * @param jclass The FJBG class, where code was emitted - * @param sym The corresponding symbol, used for looking up pickled information - */ - def emitClass(jclass: JClass, sym: Symbol) { - addInnerClasses(jclass) - writeClass("" + sym.name, jclass.getName(), toByteArray(jclass), sym) - } - - /** Returns the ScalaSignature annotation if it must be added to this class, - * none otherwise; furthermore, it adds to `jclass` the ScalaSig marker - * attribute (marking that a scala signature annotation is present) or the - * Scala marker attribute (marking that the signature for this class is in - * another file). The annotation that is returned by this method must be - * added to the class' annotations list when generating them. - * - * @param jclass The class file that is being readied. - * @param sym The symbol for which the signature has been entered in - * the symData map. This is different than the symbol - * that is being generated in the case of a mirror class. - * @return An option that is: - * - defined and contains an annotation info of the - * ScalaSignature type, instantiated with the pickle - * signature for sym (a ScalaSig marker attribute has - * been written); - * - undefined if the jclass/sym couple must not contain a - * signature (a Scala marker attribute has been written). - */ - def scalaSignatureAddingMarker(jclass: JClass, sym: Symbol): Option[AnnotationInfo] = - currentRun.symData get sym match { - case Some(pickle) if !nme.isModuleName(newTermName(jclass.getName)) => - val scalaAttr = - fjbgContext.JOtherAttribute(jclass, jclass, tpnme.ScalaSignatureATTR.toString, - versionPickle.bytes, versionPickle.writeIndex) - jclass addAttribute scalaAttr - val scalaAnnot = { - val sigBytes = ScalaSigBytes(pickle.bytes.take(pickle.writeIndex)) - AnnotationInfo(sigBytes.sigAnnot, Nil, List((nme.bytes, sigBytes))) - } - pickledBytes += pickle.writeIndex - currentRun.symData -= sym - currentRun.symData -= sym.companionSymbol - Some(scalaAnnot) - case _ => - val markerAttr = - fjbgContext.JOtherAttribute(jclass, jclass, tpnme.ScalaATTR.toString, new Array[Byte](0), 0) - jclass addAttribute markerAttr - None - } - - private var innerClassBuffer = mutable.LinkedHashSet[Symbol]() - - /** Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents. - * This is important on Android because there is otherwise an interface explosion. - */ - private def minimizeInterfaces(interfaces: List[Symbol]): List[Symbol] = { - var rest = interfaces - var leaves = List.empty[Symbol] - while(!rest.isEmpty) { - val candidate = rest.head - val nonLeaf = leaves exists { lsym => lsym isSubClass candidate } - if(!nonLeaf) { - leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym }) - } - rest = rest.tail - } - - leaves - } - - def genClass(c: IClass) { - clasz = c - innerClassBuffer.clear() - - val name = javaName(c.symbol) - - val ps = c.symbol.info.parents - - val superClass: Symbol = if(ps.isEmpty) ObjectClass else ps.head.typeSymbol; - - val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses; - val superInterfaces = superInterfaces0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol)) distinct - - val ifaces = - if(superInterfaces.isEmpty) JClass.NO_INTERFACES - else mkArray(minimizeInterfaces(superInterfaces) map javaName) - - jclass = fjbgContext.JClass(javaFlags(c.symbol), - name, - javaName(superClass), - ifaces, - c.cunit.source.toString) - - if (isStaticModule(c.symbol) || serialVUID != None || isParcelableClass) { - if (isStaticModule(c.symbol)) - addModuleInstanceField - addStaticInit(jclass, c.lookupStaticCtor) - - if (isTopLevelModule(c.symbol)) { - if (c.symbol.companionClass == NoSymbol) - generateMirrorClass(c.symbol, c.cunit.source) - else - log("No mirror class for module with linked class: " + - c.symbol.fullName) - } - } - else { - c.lookupStaticCtor foreach (constructor => addStaticInit(jclass, Some(constructor))) - - // it must be a top level class (name contains no $s) - def isCandidateForForwarders(sym: Symbol): Boolean = - exitingPickler { - !(sym.name.toString contains '$') && sym.hasModuleFlag && !sym.isImplClass && !sym.isNestedClass - } - - // At some point this started throwing lots of exceptions as a compile was finishing. - // error: java.lang.AssertionError: - // assertion failed: List(object package$CompositeThrowable, object package$CompositeThrowable) - // ...is the one I've seen repeatedly. Suppressing. - val lmoc = ( - try c.symbol.companionModule - catch { case x: AssertionError => - Console.println("Suppressing failed assert: " + x) - NoSymbol - } - ) - // add static forwarders if there are no name conflicts; see bugs #363 and #1735 - if (lmoc != NoSymbol && !c.symbol.isInterface) { - if (isCandidateForForwarders(lmoc) && !settings.noForwarders.value) { - log("Adding static forwarders from '%s' to implementations in '%s'".format(c.symbol, lmoc)) - addForwarders(jclass, lmoc.moduleClass) - } - } - } - - clasz.fields foreach genField - clasz.methods foreach genMethod - - val ssa = scalaSignatureAddingMarker(jclass, c.symbol) - addGenericSignature(jclass, c.symbol, c.symbol.owner) - addAnnotations(jclass, c.symbol.annotations ++ ssa) - addEnclosingMethodAttribute(jclass, c.symbol) - emitClass(jclass, c.symbol) - - if (c.symbol hasAnnotation BeanInfoAttr) - genBeanInfoClass(c) - } - - private def addEnclosingMethodAttribute(jclass: JClass, clazz: Symbol) { - val sym = clazz.originalEnclosingMethod - if (sym.isMethod) { - debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass)) - jclass addAttribute fjbgContext.JEnclosingMethodAttribute( - jclass, - javaName(sym.enclClass), - javaName(sym), - javaType(sym) - ) - } else if (clazz.isAnonymousClass) { - val enclClass = clazz.rawowner - assert(enclClass.isClass, enclClass) - val sym = enclClass.primaryConstructor - if (sym == NoSymbol) - log("Ran out of room looking for an enclosing method for %s: no constructor here.".format( - enclClass, clazz) - ) - else { - debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass)) - jclass addAttribute fjbgContext.JEnclosingMethodAttribute( - jclass, - javaName(enclClass), - javaName(sym), - javaType(sym).asInstanceOf[JMethodType] - ) - } - } - } - - private def toByteArray(jc: JClass): Array[Byte] = { - val bos = new java.io.ByteArrayOutputStream() - val dos = new java.io.DataOutputStream(bos) - jc.writeTo(dos) - dos.close() - bos.toByteArray - } - - /** - * Generate a bean info class that describes the given class. - * - * @author Ross Judson (ross.judson@soletta.com) - */ - def genBeanInfoClass(c: IClass) { - val beanInfoClass = fjbgContext.JClass(javaFlags(c.symbol), - javaName(c.symbol) + "BeanInfo", - "scala/beans/ScalaBeanInfo", - JClass.NO_INTERFACES, - c.cunit.source.toString) - - var fieldList = List[String]() - for (f <- clasz.fields if f.symbol.hasGetter; - g = f.symbol.getter(c.symbol); - s = f.symbol.setter(c.symbol); - if g.isPublic && !(f.symbol.name startsWith "$")) // inserting $outer breaks the bean - fieldList = javaName(f.symbol) :: javaName(g) :: (if (s != NoSymbol) javaName(s) else null) :: fieldList - val methodList = - for (m <- clasz.methods - if !m.symbol.isConstructor && - m.symbol.isPublic && - !(m.symbol.name startsWith "$") && - !m.symbol.isGetter && - !m.symbol.isSetter) yield javaName(m.symbol) - - val constructor = beanInfoClass.addNewMethod(ACC_PUBLIC, "<init>", JType.VOID, new Array[JType](0), new Array[String](0)) - val jcode = constructor.getCode().asInstanceOf[JExtendedCode] - val strKind = new JObjectType(javaName(StringClass)) - val stringArrayKind = new JArrayType(strKind) - val conType = new JMethodType(JType.VOID, Array(javaType(ClassClass), stringArrayKind, stringArrayKind)) - - def push(lst:Seq[String]) { - var fi = 0 - for (f <- lst) { - jcode.emitDUP() - jcode emitPUSH fi - if (f != null) - jcode emitPUSH f - else - jcode.emitACONST_NULL() - jcode emitASTORE strKind - fi += 1 - } - } - - jcode.emitALOAD_0() - // push the class - jcode emitPUSH javaType(c.symbol).asInstanceOf[JReferenceType] - - // push the string array of field information - jcode emitPUSH fieldList.length - jcode emitANEWARRAY strKind - push(fieldList) - - // push the string array of method information - jcode emitPUSH methodList.length - jcode emitANEWARRAY strKind - push(methodList) - - // invoke the superclass constructor, which will do the - // necessary java reflection and create Method objects. - jcode.emitINVOKESPECIAL("scala/beans/ScalaBeanInfo", "<init>", conType) - jcode.emitRETURN() - - // write the bean information class file. - writeClass("BeanInfo ", beanInfoClass.getName(), toByteArray(beanInfoClass), c.symbol) - } - - /** Add the given 'throws' attributes to jmethod */ - def addExceptionsAttribute(jmethod: JMethod, excs: List[AnnotationInfo]) { - if (excs.isEmpty) return - - val cpool = jmethod.getConstantPool - val buf: ByteBuffer = ByteBuffer.allocate(512) - var nattr = 0 - - // put some random value; the actual number is determined at the end - buf putShort 0xbaba.toShort - - for (ThrownException(exc) <- excs.distinct) { - buf.putShort( - cpool.addClass( - javaName(exc)).shortValue) - nattr += 1 - } - - assert(nattr > 0, nattr) - buf.putShort(0, nattr.toShort) - addAttribute(jmethod, tpnme.ExceptionsATTR, buf) - } - - /** Whether an annotation should be emitted as a Java annotation - * .initialize: if 'annot' is read from pickle, atp might be un-initialized - */ - private def shouldEmitAnnotation(annot: AnnotationInfo) = - annot.symbol.initialize.isJavaDefined && - annot.matches(ClassfileAnnotationClass) && - annot.args.isEmpty - - private def emitJavaAnnotations(cpool: JConstantPool, buf: ByteBuffer, annotations: List[AnnotationInfo]): Int = { - def emitArgument(arg: ClassfileAnnotArg): Unit = arg match { - case LiteralAnnotArg(const) => - const.tag match { - case BooleanTag => - buf put 'Z'.toByte - buf putShort cpool.addInteger(if(const.booleanValue) 1 else 0).toShort - case ByteTag => - buf put 'B'.toByte - buf putShort cpool.addInteger(const.byteValue).toShort - case ShortTag => - buf put 'S'.toByte - buf putShort cpool.addInteger(const.shortValue).toShort - case CharTag => - buf put 'C'.toByte - buf putShort cpool.addInteger(const.charValue).toShort - case IntTag => - buf put 'I'.toByte - buf putShort cpool.addInteger(const.intValue).toShort - case LongTag => - buf put 'J'.toByte - buf putShort cpool.addLong(const.longValue).toShort - case FloatTag => - buf put 'F'.toByte - buf putShort cpool.addFloat(const.floatValue).toShort - case DoubleTag => - buf put 'D'.toByte - buf putShort cpool.addDouble(const.doubleValue).toShort - case StringTag => - buf put 's'.toByte - buf putShort cpool.addUtf8(const.stringValue).toShort - case ClazzTag => - buf put 'c'.toByte - buf putShort cpool.addUtf8(javaType(const.typeValue).getSignature()).toShort - case EnumTag => - buf put 'e'.toByte - buf putShort cpool.addUtf8(javaType(const.tpe).getSignature()).toShort - buf putShort cpool.addUtf8(const.symbolValue.name.toString).toShort - } - - case sb@ScalaSigBytes(bytes) if !sb.isLong => - buf put 's'.toByte - buf putShort cpool.addUtf8(sb.encodedBytes).toShort - - case sb@ScalaSigBytes(bytes) if sb.isLong => - buf put '['.toByte - val stringCount = (sb.encodedBytes.length / 65534) + 1 - buf putShort stringCount.toShort - for (i <- 0 until stringCount) { - buf put 's'.toByte - val j = i * 65535 - val string = sb.encodedBytes.slice(j, j + 65535) - buf putShort cpool.addUtf8(string).toShort - } - - case ArrayAnnotArg(args) => - buf put '['.toByte - buf putShort args.length.toShort - args foreach emitArgument - - case NestedAnnotArg(annInfo) => - buf put '@'.toByte - emitAnnotation(annInfo) - } - - def emitAnnotation(annotInfo: AnnotationInfo) { - val AnnotationInfo(typ, args, assocs) = annotInfo - val jtype = javaType(typ) - buf putShort cpool.addUtf8(jtype.getSignature()).toShort - assert(args.isEmpty, args) - buf putShort assocs.length.toShort - for ((name, value) <- assocs) { - buf putShort cpool.addUtf8(name.toString).toShort - emitArgument(value) - } - } - - var nannots = 0 - val pos = buf.position() - - // put some random value; the actual number of annotations is determined at the end - buf putShort 0xbaba.toShort - - for (annot <- annotations if shouldEmitAnnotation(annot)) { - nannots += 1 - emitAnnotation(annot) - } - - // save the number of annotations - buf.putShort(pos, nannots.toShort) - nannots - } - - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig.value - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def addGenericSignature(jmember: JMember, sym: Symbol, owner: Symbol) { - if (needsGenericSignature(sym)) { - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - erasure.javaSig(sym, memberTpe) foreach { sig => - // This seems useful enough in the general case. - log(sig) - if (checkSignatures) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - clasz.cunit.warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return - } - } - val index = jmember.getConstantPool.addUtf8(sig).toShort - if (settings.verbose.value && settings.debug.value) - enteringErasure(println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)) - - val buf = ByteBuffer.allocate(2) - buf putShort index - addAttribute(jmember, tpnme.SignatureATTR, buf) - } - } - } - - def addAnnotations(jmember: JMember, annotations: List[AnnotationInfo]) { - if (annotations exists (_ matches definitions.DeprecatedAttr)) { - val attr = jmember.getContext().JOtherAttribute( - jmember.getJClass(), jmember, tpnme.DeprecatedATTR.toString, - new Array[Byte](0), 0) - jmember addAttribute attr - } - - val toEmit = annotations filter shouldEmitAnnotation - if (toEmit.isEmpty) return - - val buf: ByteBuffer = ByteBuffer.allocate(2048) - emitJavaAnnotations(jmember.getConstantPool, buf, toEmit) - addAttribute(jmember, tpnme.RuntimeAnnotationATTR, buf) - } - - def addParamAnnotations(jmethod: JMethod, pannotss: List[List[AnnotationInfo]]) { - val annotations = pannotss map (_ filter shouldEmitAnnotation) - if (annotations forall (_.isEmpty)) return - - val buf: ByteBuffer = ByteBuffer.allocate(2048) - - // number of parameters - buf.put(annotations.length.toByte) - for (annots <- annotations) - emitJavaAnnotations(jmethod.getConstantPool, buf, annots) - - addAttribute(jmethod, tpnme.RuntimeParamAnnotationATTR, buf) - } - - def addAttribute(jmember: JMember, name: Name, buf: ByteBuffer) { - if (buf.position() < 2) - return - - val length = buf.position() - val arr = buf.array().slice(0, length) - - val attr = jmember.getContext().JOtherAttribute(jmember.getJClass(), - jmember, - name.toString, - arr, - length) - jmember addAttribute attr - } - - def addInnerClasses(jclass: JClass) { - /** The outer name for this inner class. Note that it returns null - * when the inner class should not get an index in the constant pool. - * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS. - */ - def outerName(innerSym: Symbol): String = { - if (innerSym.originalEnclosingMethod != NoSymbol) - null - else { - val outerName = javaName(innerSym.rawowner) - if (isTopLevelModule(innerSym.rawowner)) "" + nme.stripModuleSuffix(newTermName(outerName)) - else outerName - } - } - - def innerName(innerSym: Symbol): String = - if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction) - null - else - innerSym.rawname + innerSym.moduleSuffix - - // add inner classes which might not have been referenced yet - exitingErasure { - for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) - innerClassBuffer += m - } - - val allInners = innerClassBuffer.toList - if (allInners.nonEmpty) { - debuglog(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.") - val innerClassesAttr = jclass.getInnerClasses() - // sort them so inner classes succeed their enclosing class - // to satisfy the Eclipse Java compiler - for (innerSym <- allInners sortBy (_.name.length)) { - val flags = { - val staticFlag = if (innerSym.rawowner.hasModuleFlag) ACC_STATIC else 0 - (javaFlags(innerSym) | staticFlag) & INNER_CLASSES_FLAGS - } - val jname = javaName(innerSym) - val oname = outerName(innerSym) - val iname = innerName(innerSym) - - // Mimicking javap inner class output - debuglog( - if (oname == null || iname == null) "//class " + jname - else "//%s=class %s of class %s".format(iname, jname, oname) - ) - - innerClassesAttr.addEntry(jname, oname, iname, flags) - } - } - } - - def genField(f: IField) { - debuglog("Adding field: " + f.symbol.fullName) - - val jfield = jclass.addNewField( - javaFieldFlags(f.symbol), - javaName(f.symbol), - javaType(f.symbol.tpe) - ) - - addGenericSignature(jfield, f.symbol, clasz.symbol) - addAnnotations(jfield, f.symbol.annotations) - } - - def genMethod(m: IMethod) { - if (m.symbol.isStaticConstructor || definitions.isGetClass(m.symbol)) return - - debuglog("Generating method " + m.symbol.fullName) - method = m - endPC.clear - computeLocalVarsIndex(m) - - var resTpe = javaType(m.symbol.tpe.resultType) - if (m.symbol.isClassConstructor) - resTpe = JType.VOID - - var flags = javaFlags(m.symbol) - if (jclass.isInterface) - flags |= ACC_ABSTRACT - - if (m.symbol.isStrictFP) - flags |= ACC_STRICT - - // native methods of objects are generated in mirror classes - if (method.native) - flags |= ACC_NATIVE - - jmethod = jclass.addNewMethod(flags, - javaName(m.symbol), - resTpe, - mkArray(m.params map (p => javaType(p.kind))), - mkArray(m.params map (p => javaName(p.sym)))) - - addRemoteException(jmethod, m.symbol) - - if (!jmethod.isAbstract() && !method.native) { - val jcode = jmethod.getCode().asInstanceOf[JExtendedCode] - - // add a fake local for debugging purposes - if (emitVars && isClosureApply(method.symbol)) { - val outerField = clasz.symbol.info.decl(nme.OUTER_LOCAL) - if (outerField != NoSymbol) { - log("Adding fake local to represent outer 'this' for closure " + clasz) - val _this = new Local( - method.symbol.newVariable(nme.FAKE_LOCAL_THIS), toTypeKind(outerField.tpe), false) - m.locals = m.locals ::: List(_this) - computeLocalVarsIndex(m) // since we added a new local, we need to recompute indexes - - jcode.emitALOAD_0() - jcode.emitGETFIELD(javaName(clasz.symbol), - javaName(outerField), - javaType(outerField)) - jcode.emitSTORE(indexOf(_this), javaType(_this.kind)) - } - } - - for (local <- m.locals if ! m.params.contains(local)) { - debuglog("add local var: " + local) - jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym)) - } - - genCode(m) - if (emitVars) - genLocalVariableTable(m, jcode) - } - - addGenericSignature(jmethod, m.symbol, clasz.symbol) - val (excs, others) = m.symbol.annotations partition (_.symbol == ThrowsClass) - addExceptionsAttribute(jmethod, excs) - addAnnotations(jmethod, others) - addParamAnnotations(jmethod, m.params.map(_.sym.annotations)) - - // check for code size - try jmethod.freeze() - catch { - case e: JCode.CodeSizeTooBigException => - clasz.cunit.error(m.symbol.pos, "Code size exceeds JVM limits: %d".format(e.codeSize)) - throw e - } - } - - /** Adds a @remote annotation, actual use unknown. - */ - private def addRemoteException(jmethod: JMethod, meth: Symbol) { - val needsAnnotation = ( - (isRemoteClass || (meth hasAnnotation RemoteAttr) && jmethod.isPublic) - && !(meth.throwsAnnotations contains RemoteExceptionClass) - ) - if (needsAnnotation) { - val c = Constant(RemoteExceptionClass.tpe) - val arg = Literal(c) setType c.tpe - meth.addAnnotation(ThrowsClass, arg) - } - } - - private def isClosureApply(sym: Symbol): Boolean = { - (sym.name == nme.apply) && - sym.owner.isSynthetic && - sym.owner.tpe.parents.exists { t => - val TypeRef(_, sym, _) = t - FunctionClass contains sym - } - } - - def addModuleInstanceField() { - jclass.addNewField(PublicStaticFinal, - nme.MODULE_INSTANCE_FIELD.toString, - jclass.getType()) - } - - def addStaticInit(cls: JClass, mopt: Option[IMethod]) { - val clinitMethod = cls.addNewMethod(PublicStatic, - "<clinit>", - JType.VOID, - JType.EMPTY_ARRAY, - new Array[String](0)) - val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode] - - mopt match { - case Some(m) => - val oldLastBlock = m.lastBlock - val lastBlock = m.newBlock() - oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) - - if (isStaticModule(clasz.symbol)) { - // call object's private ctor from static ctor - lastBlock emit NEW(REFERENCE(m.symbol.enclClass)) - lastBlock emit CALL_METHOD(m.symbol.enclClass.primaryConstructor, Static(true)) - } - - // add serialVUID code - serialVUID foreach { value => - import Flags._, definitions._ - val fieldName = "serialVersionUID" - val fieldSymbol = clasz.symbol.newValue(newTermName(fieldName), NoPosition, STATIC | FINAL) setInfo LongClass.tpe - clasz addField new IField(fieldSymbol) - lastBlock emit CONSTANT(Constant(value)) - lastBlock emit STORE_FIELD(fieldSymbol, true) - } - - if (isParcelableClass) - addCreatorCode(BytecodeGenerator.this, lastBlock) - - lastBlock emit RETURN(UNIT) - lastBlock.close - - method = m - jmethod = clinitMethod - genCode(m) - case None => - legacyStaticInitializer(cls, clinit) - } - } - - private def legacyStaticInitializer(cls: JClass, clinit: JExtendedCode) { - if (isStaticModule(clasz.symbol)) { - clinit emitNEW cls.getName() - clinit.emitINVOKESPECIAL(cls.getName(), - JMethod.INSTANCE_CONSTRUCTOR_NAME, - JMethodType.ARGLESS_VOID_FUNCTION) - } - - serialVUID foreach { value => - val fieldName = "serialVersionUID" - jclass.addNewField(PublicStaticFinal, fieldName, JType.LONG) - clinit emitPUSH value - clinit.emitPUSH(value) - clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG) - } - - if (isParcelableClass) - legacyAddCreatorCode(BytecodeGenerator.this, clinit) - - clinit.emitRETURN() - } - - /** Add a forwarder for method m */ - def addForwarder(jclass: JClass, module: Symbol, m: Symbol) { - val moduleName = javaName(module) - val methodInfo = module.thisType.memberInfo(m) - val paramJavaTypes = methodInfo.paramTypes map javaType - val paramNames = 0 until paramJavaTypes.length map ("x_" + _) - // TODO: evaluate the other flags we might be dropping on the floor here. - val flags = PublicStatic | ( - if (m.isVarargsMethod) ACC_VARARGS else 0 - ) - - /** Forwarders must not be marked final, as the JVM will not allow - * redefinition of a final static method, and we don't know what classes - * might be subclassing the companion class. See SI-4827. - */ - val mirrorMethod = jclass.addNewMethod( - flags, - javaName(m), - javaType(methodInfo.resultType), - mkArray(paramJavaTypes), - mkArray(paramNames)) - val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode] - mirrorCode.emitGETSTATIC(moduleName, - nme.MODULE_INSTANCE_FIELD.toString, - new JObjectType(moduleName)) - - var i = 0 - var index = 0 - val argTypes = mirrorMethod.getArgumentTypes() - while (i < argTypes.length) { - mirrorCode.emitLOAD(index, argTypes(i)) - index += argTypes(i).getSize() - i += 1 - } - - mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName, javaType(m).asInstanceOf[JMethodType]) - mirrorCode emitRETURN mirrorMethod.getReturnType() - - addRemoteException(mirrorMethod, m) - // only add generic signature if the method is concrete; bug #1745 - if (!m.isDeferred) - addGenericSignature(mirrorMethod, m, module) - - val (throws, others) = m.annotations partition (_.symbol == ThrowsClass) - addExceptionsAttribute(mirrorMethod, throws) - addAnnotations(mirrorMethod, others) - addParamAnnotations(mirrorMethod, m.info.params.map(_.annotations)) - } - - /** Add forwarders for all methods defined in `module` that don't conflict - * with methods in the companion class of `module`. A conflict arises when - * a method with the same name is defined both in a class and its companion - * object: method signature is not taken into account. - */ - def addForwarders(jclass: JClass, moduleClass: Symbol) { - assert(moduleClass.isModuleClass, moduleClass) - debuglog("Dumping mirror class for object: " + moduleClass) - - val className = jclass.getName - val linkedClass = moduleClass.companionClass - lazy val conflictingNames: Set[Name] = { - linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name } toSet - } - debuglog("Potentially conflicting names for forwarders: " + conflictingNames) - - for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flags.METHOD)) { - if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor) - debuglog("No forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) - else if (conflictingNames(m.name)) - log("No forwarder for " + m + " due to conflict with " + linkedClass.info.member(m.name)) - else { - log("Adding static forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) - addForwarder(jclass, moduleClass, m) - } - } - } - - /** Generate a mirror class for a top-level module. A mirror class is a class - * containing only static methods that forward to the corresponding method - * on the MODULE instance of the given Scala object. It will only be - * generated if there is no companion class: if there is, an attempt will - * instead be made to add the forwarder methods to the companion class. - */ - def generateMirrorClass(clasz: Symbol, sourceFile: SourceFile) { - import JAccessFlags._ - /* We need to save inner classes buffer and create a new one to make sure - * that we do confuse inner classes of the class we mirror with inner - * classes of the class we are mirroring. These two sets can be different - * as seen in this case: - * - * class A { - * class B - * def b: B = new B - * } - * object C extends A - * - * Here mirror class of C has a static forwarder for (inherited) method `b` - * therefore it refers to class `B` and needs InnerClasses entry. However, - * the real class for `C` (named `C$`) is empty and does not refer to `B` - * thus does not need InnerClasses entry it. - * - * NOTE: This logic has been refactored in GenASM and everything is - * implemented in a much cleaner way by having two separate buffers. - */ - val savedInnerClasses = innerClassBuffer - innerClassBuffer = mutable.LinkedHashSet[Symbol]() - val moduleName = javaName(clasz) // + "$" - val mirrorName = moduleName.substring(0, moduleName.length() - 1) - val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL, - mirrorName, - JAVA_LANG_OBJECT.getName, - JClass.NO_INTERFACES, - "" + sourceFile) - - log("Dumping mirror class for '%s'".format(mirrorClass.getName)) - addForwarders(mirrorClass, clasz) - val ssa = scalaSignatureAddingMarker(mirrorClass, clasz.companionSymbol) - addAnnotations(mirrorClass, clasz.annotations ++ ssa) - emitClass(mirrorClass, clasz) - innerClassBuffer = savedInnerClasses - } - - var linearization: List[BasicBlock] = Nil - var isModuleInitialized = false - - def genCode(m: IMethod) { - val jcode = jmethod.getCode.asInstanceOf[JExtendedCode] - - def makeLabels(bs: List[BasicBlock]) = { - debuglog("Making labels for: " + method) - - mutable.HashMap(bs map (_ -> jcode.newLabel) : _*) - } - - isModuleInitialized = false - - linearization = linearizer.linearize(m) - val labels = makeLabels(linearization) - - var nextBlock: BasicBlock = linearization.head - - def genBlocks(l: List[BasicBlock]): Unit = l match { - case Nil => () - case x :: Nil => nextBlock = null; genBlock(x) - case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys) - } - - /** Generate exception handlers for the current method. */ - def genExceptionHandlers() { - - /** Return a list of pairs of intervals where the handler is active. - * The intervals in the list have to be inclusive in the beginning and - * exclusive in the end: [start, end). - */ - def ranges(e: ExceptionHandler): List[(Int, Int)] = { - var covered = e.covered - var ranges: List[(Int, Int)] = Nil - var start = -1 - var end = -1 - - linearization foreach { b => - if (! (covered contains b) ) { - if (start >= 0) { // we're inside a handler range - end = labels(b).getAnchor() - ranges ::= ((start, end)) - start = -1 - } - } else { - if (start < 0) // we're not inside a handler range - start = labels(b).getAnchor() - - end = endPC(b) - covered -= b - } - } - - /* Add the last interval. Note that since the intervals are - * open-ended to the right, we have to give a number past the actual - * code! - */ - if (start >= 0) { - ranges ::= ((start, jcode.getPC())) - } - - if (!covered.isEmpty) - debuglog("Some covered blocks were not found in method: " + method + - " covered: " + covered + " not in " + linearization) - ranges - } - - for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) { - if (p._1 < p._2) { - debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + - " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); - val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null - else javaName(e.cls) - jcode.addExceptionHandler(p._1, p._2, - labels(e.startBlock).getAnchor(), - cls) - } else - log("Empty exception range: " + p) - } - } - - def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = { - target.isPublic || target.isProtected && { - (site.enclClass isSubClass target.enclClass) || - (site.enclosingPackage == target.privateWithin) - } - } - - def genCallMethod(call: CALL_METHOD) { - val CALL_METHOD(method, style) = call - val siteSymbol = clasz.symbol - val hostSymbol = call.hostClass - val methodOwner = method.owner - // info calls so that types are up to date; erasure may add lateINTERFACE to traits - hostSymbol.info ; methodOwner.info - - def isInterfaceCall(sym: Symbol) = ( - sym.isInterface && methodOwner != ObjectClass - || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) - ) - // whether to reference the type of the receiver or - // the type of the method owner (if not an interface!) - val useMethodOwner = ( - style != Dynamic - || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) - || hostSymbol.isBottomClass - ) - val receiver = if (useMethodOwner) methodOwner else hostSymbol - val jowner = javaName(receiver) - val jname = javaName(method) - val jtype = javaType(method).asInstanceOf[JMethodType] - - def dbg(invoke: String) { - debuglog("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype)) - } - - def initModule() { - // we initialize the MODULE$ field immediately after the super ctor - if (isStaticModule(siteSymbol) && !isModuleInitialized && - jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME && - jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) { - isModuleInitialized = true - jcode.emitALOAD_0() - jcode.emitPUTSTATIC(jclass.getName(), - nme.MODULE_INSTANCE_FIELD.toString, - jclass.getType()) - } - } - - style match { - case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) - case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) - case Dynamic if isInterfaceCall(receiver) => dbg("invokinterface"); jcode.emitINVOKEINTERFACE(jowner, jname, jtype) - case Dynamic => dbg("invokevirtual"); jcode.emitINVOKEVIRTUAL(jowner, jname, jtype) - case SuperCall(_) => - dbg("invokespecial") - jcode.emitINVOKESPECIAL(jowner, jname, jtype) - initModule() - } - } - - def genBlock(b: BasicBlock) { - labels(b).anchorToNext() - - debuglog("Generating code for block: " + b + " at pc: " + labels(b).getAnchor()) - var lastMappedPC = 0 - var lastLineNr = 0 - var crtPC = 0 - - /** local variables whose scope appears in this block. */ - val varsInBlock: mutable.Set[Local] = new mutable.HashSet - val lastInstr = b.lastInstruction - - for (instr <- b) { - instr match { - case THIS(clasz) => jcode.emitALOAD_0() - - case CONSTANT(const) => genConstant(jcode, const) - - case LOAD_ARRAY_ITEM(kind) => - if(kind.isRefOrArrayType) { jcode.emitAALOAD() } - else { - (kind: @unchecked) match { - case UNIT => throw new IllegalArgumentException("invalid type for aload " + kind) - case BOOL | BYTE => jcode.emitBALOAD() - case SHORT => jcode.emitSALOAD() - case CHAR => jcode.emitCALOAD() - case INT => jcode.emitIALOAD() - case LONG => jcode.emitLALOAD() - case FLOAT => jcode.emitFALOAD() - case DOUBLE => jcode.emitDALOAD() - } - } - - case LOAD_LOCAL(local) => jcode.emitLOAD(indexOf(local), javaType(local.kind)) - - case lf @ LOAD_FIELD(field, isStatic) => - val owner = javaName(lf.hostClass) - debuglog("LOAD_FIELD with owner: " + owner + - " flags: " + field.owner.flagString) - val fieldJName = javaName(field) - val fieldJType = javaType(field) - if (isStatic) jcode.emitGETSTATIC(owner, fieldJName, fieldJType) - else jcode.emitGETFIELD( owner, fieldJName, fieldJType) - - case LOAD_MODULE(module) => - // assert(module.isModule, "Expected module: " + module) - debuglog("generating LOAD_MODULE for: " + module + " flags: " + module.flagString); - if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString) - jcode.emitALOAD_0() - else - jcode.emitGETSTATIC(javaName(module) /* + "$" */ , - nme.MODULE_INSTANCE_FIELD.toString, - javaType(module)) - - case STORE_ARRAY_ITEM(kind) => - if(kind.isRefOrArrayType) { jcode.emitAASTORE() } - else { - (kind: @unchecked) match { - case UNIT => throw new IllegalArgumentException("invalid type for astore " + kind) - case BOOL | BYTE => jcode.emitBASTORE() - case SHORT => jcode.emitSASTORE() - case CHAR => jcode.emitCASTORE() - case INT => jcode.emitIASTORE() - case LONG => jcode.emitLASTORE() - case FLOAT => jcode.emitFASTORE() - case DOUBLE => jcode.emitDASTORE() - } - } - - case STORE_LOCAL(local) => - jcode.emitSTORE(indexOf(local), javaType(local.kind)) - - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - jcode.emitASTORE_0() - - case STORE_FIELD(field, isStatic) => - val owner = javaName(field.owner) - val fieldJName = javaName(field) - val fieldJType = javaType(field) - if (isStatic) jcode.emitPUTSTATIC(owner, fieldJName, fieldJType) - else jcode.emitPUTFIELD( owner, fieldJName, fieldJType) - - case CALL_PRIMITIVE(primitive) => genPrimitive(primitive, instr.pos) - - /** Special handling to access native Array.clone() */ - case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => - val target: String = javaType(call.targetTypeKind).getSignature() - jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType) - - case call @ CALL_METHOD(method, style) => genCallMethod(call) - - case BOX(kind) => - val Pair(mname, mtype) = jBoxTo(kind) - jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype) - - case UNBOX(kind) => - val Pair(mname, mtype) = jUnboxTo(kind) - jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype) - - case NEW(REFERENCE(cls)) => - val className = javaName(cls) - jcode emitNEW className - - case CREATE_ARRAY(elem, 1) => - if(elem.isRefOrArrayType) { jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType] } - else { jcode emitNEWARRAY javaType(elem) } - - case CREATE_ARRAY(elem, dims) => - jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims) - - case IS_INSTANCE(tpe) => - tpe match { - case REFERENCE(cls) => jcode emitINSTANCEOF new JObjectType(javaName(cls)) - case ARRAY(elem) => jcode emitINSTANCEOF new JArrayType(javaType(elem)) - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) - } - - case CHECK_CAST(tpe) => - tpe match { - case REFERENCE(cls) => if (cls != ObjectClass) { jcode emitCHECKCAST new JObjectType(javaName(cls)) } // No need to checkcast for Objects - case ARRAY(elem) => jcode emitCHECKCAST new JArrayType(javaType(elem)) - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) - } - - case SWITCH(tags, branches) => - val tagArray = new Array[Array[Int]](tags.length) - var caze = tags - var i = 0 - - while (i < tagArray.length) { - tagArray(i) = new Array[Int](caze.head.length) - caze.head.copyToArray(tagArray(i), 0) - i += 1 - caze = caze.tail - } - val branchArray = jcode.newLabels(tagArray.length) - i = 0 - while (i < branchArray.length) { - branchArray(i) = labels(branches(i)) - i += 1 - } - debuglog("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches) - jcode.emitSWITCH(tagArray, - branchArray, - labels(branches.last), - MIN_SWITCH_DENSITY) - () - - case JUMP(whereto) => - if (nextBlock != whereto) - jcode.emitGOTO_maybe_W(labels(whereto), false) // default to short jumps - - case CJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF_ICMP(conds(cond.negate()), labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ICMP(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - if (nextBlock == success) { - jcode.emitIF_ACMP(conds(cond.negate()), labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ACMP(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } else { - (kind: @unchecked) match { - case LONG => jcode.emitLCMP() - case FLOAT => - if (cond == LT || cond == LE) jcode.emitFCMPG() - else jcode.emitFCMPL() - case DOUBLE => - if (cond == LT || cond == LE) jcode.emitDCMPG() - else jcode.emitDCMPL() - } - if (nextBlock == success) { - jcode.emitIF(conds(cond.negate()), labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF(conds(cond), labels(success)); - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } - - case CZJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF(conds(cond.negate()), labels(failure)) - } else { - jcode.emitIF(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - val Success = success - val Failure = failure - (cond, nextBlock) match { - case (EQ, Success) => jcode emitIFNONNULL labels(failure) - case (NE, Failure) => jcode emitIFNONNULL labels(success) - case (EQ, Failure) => jcode emitIFNULL labels(success) - case (NE, Success) => jcode emitIFNULL labels(failure) - case (EQ, _) => - jcode emitIFNULL labels(success) - jcode.emitGOTO_maybe_W(labels(failure), false) - case (NE, _) => - jcode emitIFNONNULL labels(success) - jcode.emitGOTO_maybe_W(labels(failure), false) - case _ => - } - } else { - (kind: @unchecked) match { - case LONG => - jcode.emitLCONST_0() - jcode.emitLCMP() - case FLOAT => - jcode.emitFCONST_0() - if (cond == LT || cond == LE) jcode.emitFCMPG() - else jcode.emitFCMPL() - case DOUBLE => - jcode.emitDCONST_0() - if (cond == LT || cond == LE) jcode.emitDCMPG() - else jcode.emitDCMPL() - } - if (nextBlock == success) { - jcode.emitIF(conds(cond.negate()), labels(failure)) - } else { - jcode.emitIF(conds(cond), labels(success)) - if (nextBlock != failure) - jcode.emitGOTO_maybe_W(labels(failure), false) - } - } - - case RETURN(kind) => jcode emitRETURN javaType(kind) - - case THROW(_) => jcode.emitATHROW() - - case DROP(kind) => - if(kind.isWideType) jcode.emitPOP2() - else jcode.emitPOP() - - case DUP(kind) => - if(kind.isWideType) jcode.emitDUP2() - else jcode.emitDUP() - - case MONITOR_ENTER() => jcode.emitMONITORENTER() - - case MONITOR_EXIT() => jcode.emitMONITOREXIT() - - case SCOPE_ENTER(lv) => - varsInBlock += lv - lv.start = jcode.getPC() - - case SCOPE_EXIT(lv) => - if (varsInBlock(lv)) { - lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges - varsInBlock -= lv - } - else if (b.varsInScope(lv)) { - lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges - b.varsInScope -= lv - } - else dumpMethodAndAbort(method, "Illegal local var nesting") - - case LOAD_EXCEPTION(_) => - () - } - - crtPC = jcode.getPC() - - // assert(instr.pos.source.isEmpty || instr.pos.source.get == (clasz.cunit.source), "sources don't match") - // val crtLine = instr.pos.line.get(lastLineNr); - - val crtLine = try { - if (instr.pos == NoPosition) lastLineNr else (instr.pos).line // check NoPosition to avoid costly exception - } catch { - case _: UnsupportedOperationException => - log("Warning: wrong position in: " + method) - lastLineNr - } - - if (instr eq lastInstr) { endPC(b) = jcode.getPC() } - - //System.err.println("CRTLINE: " + instr.pos + " " + - // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); - - if (crtPC > lastMappedPC) { - jcode.completeLineNumber(lastMappedPC, crtPC, crtLine) - lastMappedPC = crtPC - lastLineNr = crtLine - } - } - - // local vars that survived this basic block - for (lv <- varsInBlock) { - lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges - } - for (lv <- b.varsInScope) { - lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges - } - } - - def genPrimitive(primitive: Primitive, pos: Position) { - primitive match { - case Negation(kind) => - if(kind.isIntSizedType) { jcode.emitINEG() } - else { - kind match { - case LONG => jcode.emitLNEG() - case FLOAT => jcode.emitFNEG() - case DOUBLE => jcode.emitDNEG() - case _ => abort("Impossible to negate a " + kind) - } - } - - case Arithmetic(op, kind) => - op match { - case ADD => - if(kind.isIntSizedType) { jcode.emitIADD() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLADD() - case FLOAT => jcode.emitFADD() - case DOUBLE => jcode.emitDADD() - } - } - - case SUB => - if(kind.isIntSizedType) { jcode.emitISUB() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLSUB() - case FLOAT => jcode.emitFSUB() - case DOUBLE => jcode.emitDSUB() - } - } - - case MUL => - if(kind.isIntSizedType) { jcode.emitIMUL() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLMUL() - case FLOAT => jcode.emitFMUL() - case DOUBLE => jcode.emitDMUL() - } - } - - case DIV => - if(kind.isIntSizedType) { jcode.emitIDIV() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLDIV() - case FLOAT => jcode.emitFDIV() - case DOUBLE => jcode.emitDDIV() - } - } - - case REM => - if(kind.isIntSizedType) { jcode.emitIREM() } - else { - (kind: @unchecked) match { - case LONG => jcode.emitLREM() - case FLOAT => jcode.emitFREM() - case DOUBLE => jcode.emitDREM() - } - } - - case NOT => - if(kind.isIntSizedType) { - jcode.emitPUSH(-1) - jcode.emitIXOR() - } else if(kind == LONG) { - jcode.emitPUSH(-1l) - jcode.emitLXOR() - } else { - abort("Impossible to negate an " + kind) - } - - case _ => - abort("Unknown arithmetic primitive " + primitive) - } - - case Logical(op, kind) => ((op, kind): @unchecked) match { - case (AND, LONG) => jcode.emitLAND() - case (AND, INT) => jcode.emitIAND() - case (AND, _) => - jcode.emitIAND() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - - case (OR, LONG) => jcode.emitLOR() - case (OR, INT) => jcode.emitIOR() - case (OR, _) => - jcode.emitIOR() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - - case (XOR, LONG) => jcode.emitLXOR() - case (XOR, INT) => jcode.emitIXOR() - case (XOR, _) => - jcode.emitIXOR() - if (kind != BOOL) - jcode.emitT2T(javaType(INT), javaType(kind)); - } - - case Shift(op, kind) => ((op, kind): @unchecked) match { - case (LSL, LONG) => jcode.emitLSHL() - case (LSL, INT) => jcode.emitISHL() - case (LSL, _) => - jcode.emitISHL() - jcode.emitT2T(javaType(INT), javaType(kind)) - - case (ASR, LONG) => jcode.emitLSHR() - case (ASR, INT) => jcode.emitISHR() - case (ASR, _) => - jcode.emitISHR() - jcode.emitT2T(javaType(INT), javaType(kind)) - - case (LSR, LONG) => jcode.emitLUSHR() - case (LSR, INT) => jcode.emitIUSHR() - case (LSR, _) => - jcode.emitIUSHR() - jcode.emitT2T(javaType(INT), javaType(kind)) - } - - case Comparison(op, kind) => ((op, kind): @unchecked) match { - case (CMP, LONG) => jcode.emitLCMP() - case (CMPL, FLOAT) => jcode.emitFCMPL() - case (CMPG, FLOAT) => jcode.emitFCMPG() - case (CMPL, DOUBLE) => jcode.emitDCMPL() - case (CMPG, DOUBLE) => jcode.emitDCMPL() - } - - case Conversion(src, dst) => - debuglog("Converting from: " + src + " to: " + dst) - if (dst == BOOL) { - println("Illegal conversion at: " + clasz + " at: " + pos.source + ":" + pos.line) - } else - jcode.emitT2T(javaType(src), javaType(dst)) - - case ArrayLength(_) => - jcode.emitARRAYLENGTH() - - case StartConcat => - jcode emitNEW StringBuilderClassName - jcode.emitDUP() - jcode.emitINVOKESPECIAL(StringBuilderClassName, - JMethod.INSTANCE_CONSTRUCTOR_NAME, - JMethodType.ARGLESS_VOID_FUNCTION) - - case StringConcat(el) => - val jtype = el match { - case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT - case _ => javaType(el) - } - jcode.emitINVOKEVIRTUAL(StringBuilderClassName, - "append", - new JMethodType(StringBuilderType, - Array(jtype))) - case EndConcat => - jcode.emitINVOKEVIRTUAL(StringBuilderClassName, - "toString", - toStringType) - - case _ => - abort("Unimplemented primitive " + primitive) - } - } - - // genCode starts here - genBlocks(linearization) - - if (this.method.exh != Nil) - genExceptionHandlers; - } - - - /** Emit a Local variable table for debugging purposes. - * Synthetic locals are skipped. All variables are method-scoped. - */ - private def genLocalVariableTable(m: IMethod, jcode: JCode) { - val vars = m.locals filterNot (_.sym.isArtifact) - if (vars.isEmpty) return - - val pool = jclass.getConstantPool - val pc = jcode.getPC() - var anonCounter = 0 - var entries = 0 - vars.foreach { lv => - lv.ranges = mergeEntries(lv.ranges.reverse); - entries += lv.ranges.length - } - if (!jmethod.isStatic()) entries += 1 - - val lvTab = ByteBuffer.allocate(2 + 10 * entries) - def emitEntry(name: String, signature: String, idx: Short, start: Short, end: Short) { - lvTab putShort start - lvTab putShort end - lvTab putShort pool.addUtf8(name).toShort - lvTab putShort pool.addUtf8(signature).toShort - lvTab putShort idx - } - - lvTab.putShort(entries.toShort) - - if (!jmethod.isStatic()) { - emitEntry("this", jclass.getType().getSignature(), 0, 0.toShort, pc.toShort) - } - - for (lv <- vars) { - val name = if (javaName(lv.sym) eq null) { - anonCounter += 1 - "<anon" + anonCounter + ">" - } else javaName(lv.sym) - - val index = indexOf(lv).toShort - val tpe = javaType(lv.kind).getSignature() - for ((start, end) <- lv.ranges) { - emitEntry(name, tpe, index, start.toShort, (end - start).toShort) - } - } - val attr = - fjbgContext.JOtherAttribute(jclass, - jcode, - tpnme.LocalVariableTableATTR.toString, - lvTab.array()) - jcode addAttribute attr - } - - - /** For each basic block, the first PC address following it. */ - val endPC = new mutable.HashMap[BasicBlock, Int] - - ////////////////////// local vars /////////////////////// - - def sizeOf(k: TypeKind): Int = if(k.isWideType) 2 else 1 - - def indexOf(local: Local): Int = { - assert(local.index >= 0, "Invalid index for: " + local + "{" + local.## + "}: ") - local.index - } - - /** - * Compute the indexes of each local variable of the given - * method. *Does not assume the parameters come first!* - */ - def computeLocalVarsIndex(m: IMethod) { - var idx = if (m.symbol.isStaticMember) 0 else 1; - - for (l <- m.params) { - debuglog("Index value for " + l + "{" + l.## + "}: " + idx) - l.index = idx - idx += sizeOf(l.kind) - } - - for (l <- m.locals if !(m.params contains l)) { - debuglog("Index value for " + l + "{" + l.## + "}: " + idx) - l.index = idx - idx += sizeOf(l.kind) - } - } - - ////////////////////// Utilities //////////////////////// - - /** Merge adjacent ranges. */ - private def mergeEntries(ranges: List[(Int, Int)]): List[(Int, Int)] = - (ranges.foldLeft(Nil: List[(Int, Int)]) { (collapsed: List[(Int, Int)], p: (Int, Int)) => (collapsed, p) match { - case (Nil, _) => List(p) - case ((s1, e1) :: rest, (s2, e2)) if (e1 == s2) => (s1, e2) :: rest - case _ => p :: collapsed - }}).reverse - } - - private def mkFlags(args: Int*) = args.foldLeft(0)(_ | _) - - /** - * Return the Java modifiers for the given symbol. - * Java modifiers for classes: - * - public, abstract, final, strictfp (not used) - * for interfaces: - * - the same as for classes, without 'final' - * for fields: - * - public, private (*) - * - static, final - * for methods: - * - the same as for fields, plus: - * - abstract, synchronized (not used), strictfp (not used), native (not used) - * - * (*) protected cannot be used, since inner classes 'see' protected members, - * and they would fail verification after lifted. - */ - def javaFlags(sym: Symbol): Int = { - // constructors of module classes should be private - // PP: why are they only being marked private at this stage and not earlier? - val privateFlag = - sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) - - // Final: the only fields which can receive ACC_FINAL are eager vals. - // Neither vars nor lazy vals can, because: - // - // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3 - // "Another problem is that the specification allows aggressive - // optimization of final fields. Within a thread, it is permissible to - // reorder reads of a final field with those modifications of a final - // field that do not take place in the constructor." - // - // A var or lazy val which is marked final still has meaning to the - // scala compiler. The word final is heavily overloaded unfortunately; - // for us it means "not overridable". At present you can't override - // vars regardless; this may change. - // - // The logic does not check .isFinal (which checks flags for the FINAL flag, - // and includes symbols marked lateFINAL) instead inspecting rawflags so - // we can exclude lateFINAL. Such symbols are eligible for inlining, but to - // avoid breaking proxy software which depends on subclassing, we do not - // emit ACC_FINAL. - // Nested objects won't receive ACC_FINAL in order to allow for their overriding. - - val finalFlag = ( - (((sym.rawflags & Flags.FINAL) != 0) || isTopLevelModule(sym)) - && !sym.enclClass.isInterface - && !sym.isClassConstructor - && !sym.isMutable // lazy vals and vars both - ) - - // Primitives are "abstract final" to prohibit instantiation - // without having to provide any implementations, but that is an - // illegal combination of modifiers at the bytecode level so - // suppress final if abstract if present. - mkFlags( - if (privateFlag) ACC_PRIVATE else ACC_PUBLIC, - if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0, - if (sym.isInterface) ACC_INTERFACE else 0, - if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0, - if (sym.isStaticMember) ACC_STATIC else 0, - if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, - if (sym.isArtifact) ACC_SYNTHETIC else 0, - if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, - if (sym.isVarargsMethod) ACC_VARARGS else 0, - if (sym.hasFlag(Flags.SYNCHRONIZED)) JAVA_ACC_SYNCHRONIZED else 0 - ) - } - def javaFieldFlags(sym: Symbol) = ( - javaFlags(sym) | mkFlags( - if (sym hasAnnotation TransientAttr) ACC_TRANSIENT else 0, - if (sym hasAnnotation VolatileAttr) ACC_VOLATILE else 0, - if (sym.isMutable) 0 else ACC_FINAL - ) - ) - - def isTopLevelModule(sym: Symbol): Boolean = - exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } - - def isStaticModule(sym: Symbol): Boolean = { - sym.isModuleClass && !sym.isImplClass && !sym.isLifted - } - -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala new file mode 100644 index 0000000000..f0f91e7d1a --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala @@ -0,0 +1,97 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Jason Zaugg + */ + +package scala.tools.nsc +package backend.jvm +import scala.tools.nsc.io.AbstractFile +import scala.tools.nsc.symtab._ + +/** Code shared between the legagy backend [[scala.tools.nsc.backend.jvm.GenJVM]] + * and the new backend [[scala.tools.nsc.backend.jvm.GenASM]]. There should be + * more here, but for now I'm starting with the refactorings that are either + * straightforward to review or necessary for maintenance. + */ +trait GenJVMASM { + val global: Global + import global._ + import icodes._ + import definitions._ + + protected def outputDirectory(sym: Symbol): AbstractFile = + settings.outputDirs outputDirFor enteringFlatten(sym.sourceFile) + + protected def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { + var dir = base + val pathParts = clsName.split("[./]").toList + for (part <- pathParts.init) { + dir = dir.subdirectoryNamed(part) + } + dir.fileNamed(pathParts.last + suffix) + } + protected def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = + getFile(outputDirectory(sym), clsName, suffix) + + protected val ExcludedForwarderFlags = { + import Flags._ + // Should include DEFERRED but this breaks findMember. + ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO ) + } + + protected def isJavaEntryPoint(icls: IClass) = { + val sym = icls.symbol + def fail(msg: String, pos: Position = sym.pos) = { + icls.cunit.warning(sym.pos, + sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + + " Reason: " + msg + // TODO: make this next claim true, if possible + // by generating valid main methods as static in module classes + // not sure what the jvm allows here + // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." + ) + false + } + def failNoForwarder(msg: String) = { + fail(msg + ", which means no static forwarder can be generated.\n") + } + val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil + val hasApproximate = possibles exists { m => + m.info match { + case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass + case _ => false + } + } + // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. + hasApproximate && { + // Before erasure so we can identify generic mains. + enteringErasure { + val companion = sym.linkedClassOfClass + val companionMain = companion.tpe.member(nme.main) + + if (hasJavaMainMethod(companion)) + failNoForwarder("companion contains its own main method") + else if (companion.tpe.member(nme.main) != NoSymbol) + // this is only because forwarders aren't smart enough yet + failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") + else if (companion.isTrait) + failNoForwarder("companion is a trait") + // Now either succeeed, or issue some additional warnings for things which look like + // attempts to be java main methods. + else possibles exists { m => + m.info match { + case PolyType(_, _) => + fail("main methods cannot be generic.") + case MethodType(params, res) => + if (res.typeSymbol :: params exists (_.isAbstractType)) + fail("main methods cannot refer to type parameters or abstract types.", m.pos) + else + isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) + case tp => + fail("don't know what this is: " + tp, m.pos) + } + } + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala deleted file mode 100644 index 613f8f893e..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala +++ /dev/null @@ -1,141 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Iulian Dragos - */ - -package scala.tools.nsc -package backend.jvm - -import scala.collection.{ mutable, immutable } -import ch.epfl.lamp.fjbg._ - -trait GenJVMUtil { - self: GenJVM => - - import global._ - import icodes._ - import definitions._ - - /** Map from type kinds to the Java reference types. It is used for - * loading class constants. @see Predef.classOf. - */ - val classLiteral = immutable.Map[TypeKind, JObjectType]( - UNIT -> new JObjectType("java.lang.Void"), - BOOL -> new JObjectType("java.lang.Boolean"), - BYTE -> new JObjectType("java.lang.Byte"), - SHORT -> new JObjectType("java.lang.Short"), - CHAR -> new JObjectType("java.lang.Character"), - INT -> new JObjectType("java.lang.Integer"), - LONG -> new JObjectType("java.lang.Long"), - FLOAT -> new JObjectType("java.lang.Float"), - DOUBLE -> new JObjectType("java.lang.Double") - ) - - // Don't put this in per run caches. - private val javaNameCache = new mutable.WeakHashMap[Symbol, Name]() ++= List( - NothingClass -> binarynme.RuntimeNothing, - RuntimeNothingClass -> binarynme.RuntimeNothing, - NullClass -> binarynme.RuntimeNull, - RuntimeNullClass -> binarynme.RuntimeNull - ) - - /** This trait may be used by tools who need access to - * utility methods like javaName and javaType. (for instance, - * the Eclipse plugin uses it). - */ - trait BytecodeUtil { - - val conds = immutable.Map[TestOp, Int]( - EQ -> JExtendedCode.COND_EQ, - NE -> JExtendedCode.COND_NE, - LT -> JExtendedCode.COND_LT, - GT -> JExtendedCode.COND_GT, - LE -> JExtendedCode.COND_LE, - GE -> JExtendedCode.COND_GE - ) - - /** Specialized array conversion to prevent calling - * java.lang.reflect.Array.newInstance via TraversableOnce.toArray - */ - - def mkArray(xs: Traversable[JType]): Array[JType] = { val a = new Array[JType](xs.size); xs.copyToArray(a); a } - def mkArray(xs: Traversable[String]): Array[String] = { val a = new Array[String](xs.size); xs.copyToArray(a); a } - - /** Return the a name of this symbol that can be used on the Java - * platform. It removes spaces from names. - * - * Special handling: - * scala.Nothing erases to scala.runtime.Nothing$ - * scala.Null erases to scala.runtime.Null$ - * - * This is needed because they are not real classes, and they mean - * 'abrupt termination upon evaluation of that expression' or null respectively. - * This handling is done already in GenICode, but here we need to remove - * references from method signatures to these types, because such classes can - * not exist in the classpath: the type checker will be very confused. - */ - def javaName(sym: Symbol): String = - javaNameCache.getOrElseUpdate(sym, { - if (sym.isClass || (sym.isModule && !sym.isMethod)) - sym.javaBinaryName - else - sym.javaSimpleName - }).toString - - def javaType(t: TypeKind): JType = (t: @unchecked) match { - case UNIT => JType.VOID - case BOOL => JType.BOOLEAN - case BYTE => JType.BYTE - case SHORT => JType.SHORT - case CHAR => JType.CHAR - case INT => JType.INT - case LONG => JType.LONG - case FLOAT => JType.FLOAT - case DOUBLE => JType.DOUBLE - case REFERENCE(cls) => new JObjectType(javaName(cls)) - case ARRAY(elem) => new JArrayType(javaType(elem)) - } - - def javaType(t: Type): JType = javaType(toTypeKind(t)) - - def javaType(s: Symbol): JType = - if (s.isMethod) - new JMethodType( - if (s.isClassConstructor) JType.VOID else javaType(s.tpe.resultType), - mkArray(s.tpe.paramTypes map javaType) - ) - else - javaType(s.tpe) - - protected def genConstant(jcode: JExtendedCode, const: Constant) { - const.tag match { - case UnitTag => () - case BooleanTag => jcode emitPUSH const.booleanValue - case ByteTag => jcode emitPUSH const.byteValue - case ShortTag => jcode emitPUSH const.shortValue - case CharTag => jcode emitPUSH const.charValue - case IntTag => jcode emitPUSH const.intValue - case LongTag => jcode emitPUSH const.longValue - case FloatTag => jcode emitPUSH const.floatValue - case DoubleTag => jcode emitPUSH const.doubleValue - case StringTag => jcode emitPUSH const.stringValue - case NullTag => jcode.emitACONST_NULL() - case ClazzTag => - val kind = toTypeKind(const.typeValue) - val toPush = - if (kind.isValueType) classLiteral(kind) - else javaType(kind).asInstanceOf[JReferenceType] - - jcode emitPUSH toPush - - case EnumTag => - val sym = const.symbolValue - jcode.emitGETSTATIC(javaName(sym.owner), - javaName(sym), - javaType(sym.tpe.underlying)) - case _ => - abort("Unknown constant value: " + const) - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala deleted file mode 100644 index 2fb6550239..0000000000 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ /dev/null @@ -1,2244 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Nikolay Mihaylov - */ - - -package scala.tools.nsc -package backend.msil - -import java.io.{File, IOException} -import scala.collection.{ mutable, immutable } -import scala.tools.nsc.symtab._ - -import ch.epfl.lamp.compiler.msil.{Type => MsilType, _} -import ch.epfl.lamp.compiler.msil.emit._ -import ch.epfl.lamp.compiler.msil.util.PECustomMod -import scala.language.postfixOps - -abstract class GenMSIL extends SubComponent { - import global._ - import loaders.clrTypes - import clrTypes.{types, constructors, methods, fields} - import icodes._ - import icodes.opcodes._ - - /** Create a new phase */ - override def newPhase(p: Phase) = new MsilPhase(p) - - val phaseName = "msil" - /** MSIL code generation phase - */ - class MsilPhase(prev: Phase) extends GlobalPhase(prev) { - def name = phaseName - override def newFlags = phaseNewFlags - - override def erasedTypes = true - - override def run() { - if (settings.debug.value) inform("[running phase " + name + " on icode]") - - val codeGenerator = new BytecodeGenerator - - //classes is ICodes.classes, a HashMap[Symbol, IClass] - classes.values foreach codeGenerator.findEntryPoint - if( settings.Xshowcls.isSetByUser && (codeGenerator.entryPoint == null) ) { // TODO introduce dedicated setting instead - val entryclass = settings.Xshowcls.value.toString - warning("Couldn't find entry class " + entryclass) - } - - codeGenerator.initAssembly - - val classesSorted = classes.values.toList.sortBy(c => c.symbol.id) // simplifies comparing cross-compiler vs. .exe output - classesSorted foreach codeGenerator.createTypeBuilder - classesSorted foreach codeGenerator.createClassMembers - - try { - classesSorted foreach codeGenerator.genClass - } finally { - codeGenerator.writeAssembly - } - } - - override def apply(unit: CompilationUnit) { - abort("MSIL works on icode classes, not on compilation units!") - } - } - - /** - * MSIL bytecode generator. - * - */ - class BytecodeGenerator { - - val MODULE_INSTANCE_NAME = "MODULE$" - - import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, BYTE => MBYTE, SHORT => MSHORT, - CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT, - DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING, - STRING_ARRAY => MSTRING_ARRAY, - SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR, - SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR} - - val EXCEPTION = clrTypes.getType("System.Exception") - val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes) - - val MMONITOR = clrTypes.getType("System.Threading.Monitor") - val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT)) - val MMONITOR_EXIT = MMONITOR.GetMethod("Exit", Array(MOBJECT)) - - val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder") - val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes) - val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString", - MsilType.EmptyTypes) - - val TYPE_FROM_HANDLE = - clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle"))) - - val INT_PTR = clrTypes.getType("System.IntPtr") - - val SystemConvert = clrTypes.getType("System.Convert") - - val objParam = Array(MOBJECT) - - val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam) // see comment in emitUnbox - val toSByte: MethodInfo = SystemConvert.GetMethod("ToSByte", objParam) - val toShort: MethodInfo = SystemConvert.GetMethod("ToInt16", objParam) - val toChar: MethodInfo = SystemConvert.GetMethod("ToChar", objParam) - val toInt: MethodInfo = SystemConvert.GetMethod("ToInt32", objParam) - val toLong: MethodInfo = SystemConvert.GetMethod("ToInt64", objParam) - val toFloat: MethodInfo = SystemConvert.GetMethod("ToSingle", objParam) - val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam) - - //val boxedUnit: FieldInfo = msilType(definitions.BoxedUnitModule.info).GetField("UNIT") - val boxedUnit: FieldInfo = fields(definitions.BoxedUnit_UNIT) - - // Scala attributes - // symtab.Definitions -> object (singleton..) - val CloneableAttr = definitions.CloneableAttr.tpe - val TransientAtt = definitions.TransientAttr.tpe - // remoting: the architectures are too different, no mapping (no portable code - // possible) - - // java instance methods that are mapped to static methods in .net - // these will need to be called with OpCodes.Call (not Callvirt) - val dynToStatMapped = mutable.HashSet[Symbol]() - - initMappings() - - /** Create the mappings between java and .net classes and methods */ - private def initMappings() { - mapType(definitions.AnyClass, MOBJECT) - mapType(definitions.AnyRefClass, MOBJECT) - //mapType(definitions.NullClass, clrTypes.getType("scala.AllRef$")) - //mapType(definitions.NothingClass, clrTypes.getType("scala.All$")) - // FIXME: for some reason the upper two lines map to null - mapType(definitions.NullClass, EXCEPTION) - mapType(definitions.NothingClass, EXCEPTION) - - mapType(definitions.BooleanClass, MBOOL) - mapType(definitions.ByteClass, MBYTE) - mapType(definitions.ShortClass, MSHORT) - mapType(definitions.CharClass, MCHAR) - mapType(definitions.IntClass, MINT) - mapType(definitions.LongClass, MLONG) - mapType(definitions.FloatClass, MFLOAT) - mapType(definitions.DoubleClass, MDOUBLE) - } - - var clasz: IClass = _ - var method: IMethod = _ - - var massembly: AssemblyBuilder = _ - var mmodule: ModuleBuilder = _ - var mcode: ILGenerator = _ - - var assemName: String = _ - var firstSourceName = "" - var outDir: File = _ - var srcPath: File = _ - var moduleName: String = _ - - def initAssembly() { - - assemName = settings.assemname.value - - if (assemName == "") { - if (entryPoint != null) { - assemName = msilName(entryPoint.enclClass) - // remove the $ at the end (from module-name) - assemName = assemName.substring(0, assemName.length() - 1) - } else { - // assuming filename of first source file - assert(firstSourceName.endsWith(".scala"), firstSourceName) - assemName = firstSourceName.substring(0, firstSourceName.length() - 6) - } - } else { - if (assemName.endsWith(".msil")) - assemName = assemName.substring(0, assemName.length()-5) - if (assemName.endsWith(".il")) - assemName = assemName.substring(0, assemName.length()-3) - val f: File = new File(assemName) - assemName = f.getName() - } - - outDir = new File(settings.outdir.value) - - srcPath = new File(settings.sourcedir.value) - - val assemblyName = new AssemblyName() - assemblyName.Name = assemName - massembly = AssemblyBuilderFactory.DefineDynamicAssembly(assemblyName) - - moduleName = assemName // + (if (entryPoint == null) ".dll" else ".exe") - // filename here: .dll or .exe (in both parameters), second: give absolute-path - mmodule = massembly.DefineDynamicModule(moduleName, - new File(outDir, moduleName).getAbsolutePath()) - assert (mmodule != null) - } - - - /** - * Form of the custom Attribute parameter (Ecma-335.pdf) - * - p. 163 for CustomAttrib Form, - * - p. 164 for FixedArg Form (Array and Element) (if array or not is known!) - * !! least significant byte first if values longer than one byte !! - * - * 1: Prolog (unsigned int16, value 0x0001) -> symtab[0] = 0x01, symtab[1] = 0x00 - * 2: FixedArgs (directly the data, get number and types from related constructor) - * 2.1: length of the array (unsigned int32, 4 bytes, least significant first) - * 2.2: the byte array data - * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000) - */ - def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder) { - def addMarker() { - val markerSymtab = new Array[Byte](4) - markerSymtab(0) = 1.toByte - tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR, markerSymtab) - } - - // both conditions are needed (why exactly..?) - if (tBuilder.Name.endsWith("$") || sym.isModuleClass) { - addMarker() - } else { - currentRun.symData.get(sym) match { - case Some(pickle) => - var size = pickle.writeIndex - val symtab = new Array[Byte](size + 8) - symtab(0) = 1.toByte - for (i <- 2 until 6) { - symtab(i) = (size & 0xff).toByte - size = size >> 8 - } - java.lang.System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex) - - tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab) - - currentRun.symData -= sym - currentRun.symData -= sym.companionSymbol - - case _ => - addMarker() - } - } - } - - /** - * Mutates `member` adding CLR attributes (if any) based on sym.annotations. - * Please notice that CLR custom modifiers are a different beast (see customModifiers below) - * and thus shouldn't be added by this method. - */ - def addAttributes(member: ICustomAttributeSetter, annotations: List[AnnotationInfo]) { - // val attributes = annotations.map(_.atp.typeSymbol).collect { - // case definitions.TransientAttr => null // TODO this is just an example - // } - return // TODO: implement at some point - } - - /** - * What's a CLR custom modifier? Intro available as source comments in compiler.msil.CustomModifier. - * It's basically a marker associated with a location (think of FieldInfo, ParameterInfo, and PropertyInfo) - * and thus that marker (be it optional or required) becomes part of the signature of that location. - * Some annotations will become CLR attributes (see addAttributes above), others custom modifiers (this method). - */ - def customModifiers(annotations: List[AnnotationInfo]): Array[CustomModifier] = { - annotations.map(_.atp.typeSymbol).collect { - case definitions.VolatileAttr => new CustomModifier(true, CustomModifier.VolatileMarker) - } toArray - } - - - - /* - debuglog("creating annotations: " + annotations + " for member : " + member) - for (annot@ AnnotationInfo(typ, annArgs, nvPairs) <- annotations ; - if annot.isConstant) - //!typ.typeSymbol.isJavaDefined - { -// assert(consts.length <= 1, -// "too many constant arguments for annotations; "+consts.toString()) - - // Problem / TODO having the symbol of the annotations type would be nicer - // (i hope that type.typeSymbol is the same as the one in types2create) - // AND: this will crash if the annotations Type is already compiled (-> not a typeBuilder) - // when this is solved, types2create will be the same as icodes.classes, thus superfluous - val annType: TypeBuilder = getType(typ.typeSymbol).asInstanceOf[TypeBuilder] -// val annType: MsilType = getType(typ.typeSymbol) - - // Problem / TODO: i have no idea which constructor is used. This - // information should be available in AnnotationInfo. - annType.CreateType() // else, GetConstructors can't be used - val constr: ConstructorInfo = annType.GetConstructors()(0) - // prevent a second call of CreateType, only needed because there's no - // other way than GetConstructors()(0) to get the constructor, if there's - // no constructor symbol available. - - val args: Array[Byte] = - getAttributeArgs( - annArgs map (_.constant.get), - (for((n,v) <- nvPairs) yield (n, v.constant.get))) - member.SetCustomAttribute(constr, args) - } - } */ - -/* def getAttributeArgs(consts: List[Constant], nvPairs: List[(Name, Constant)]): Array[Byte] = { - val buf = ByteBuffer.allocate(2048) // FIXME: this may be not enough! - buf.order(java.nio.ByteOrder.LITTLE_ENDIAN) - buf.putShort(1.toShort) // signature - - def emitSerString(str: String) = { - // this is wrong, it has to be the length of the UTF-8 byte array, which - // may be longer (see clr-book on page 302) -// val length: Int = str.length - val strBytes: Array[Byte] = try { - str.getBytes("UTF-8") - } catch { - case _: Error => abort("could not get byte-array for string: " + str) - } - val length: Int = strBytes.length //this length is stored big-endian - if (length < 128) - buf.put(length.toByte) - else if (length < (1<<14)) { - buf.put(((length >> 8) | 0x80).toByte) // the bits 14 and 15 of length are '0' - buf.put((length | 0xff).toByte) - } else if (length < (1 << 29)) { - buf.put(((length >> 24) | 0xc0).toByte) - buf.put(((length >> 16) & 0xff).toByte) - buf.put(((length >> 8) & 0xff).toByte) - buf.put(((length ) & 0xff).toByte) - } else - abort("string too long for attribute parameter: " + length) - buf.put(strBytes) - } - - def emitConst(const: Constant): Unit = const.tag match { - case BooleanTag => buf.put((if (const.booleanValue) 1 else 0).toByte) - case ByteTag => buf.put(const.byteValue) - case ShortTag => buf.putShort(const.shortValue) - case CharTag => buf.putChar(const.charValue) - case IntTag => buf.putInt(const.intValue) - case LongTag => buf.putLong(const.longValue) - case FloatTag => buf.putFloat(const.floatValue) - case DoubleTag => buf.putDouble(const.doubleValue) - case StringTag => - val str: String = const.stringValue - if (str == null) { - buf.put(0xff.toByte) - } else { - emitSerString(str) - } - case ArrayTag => - val arr: Array[Constant] = const.arrayValue - if (arr == null) { - buf.putInt(0xffffffff) - } else { - buf.putInt(arr.length) - arr.foreach(emitConst) - } - - // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag, ArrayTag ??? - - case _ => abort("could not handle attribute argument: " + const) - } - - consts foreach emitConst - buf.putShort(nvPairs.length.toShort) - def emitNamedArg(nvPair: (Name, Constant)) { - // the named argument is a property of the attribute (it can't be a field, since - // all fields in scala are private) - buf.put(0x54.toByte) - - def emitType(c: Constant) = c.tag match { // type of the constant, Ecma-335.pdf, page 151 - case BooleanTag => buf.put(0x02.toByte) - case ByteTag => buf.put(0x05.toByte) - case ShortTag => buf.put(0x06.toByte) - case CharTag => buf.put(0x07.toByte) - case IntTag => buf.put(0x08.toByte) - case LongTag => buf.put(0x0a.toByte) - case FloatTag => buf.put(0x0c.toByte) - case DoubleTag => buf.put(0x0d.toByte) - case StringTag => buf.put(0x0e.toByte) - - // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag ??? - - // ArrayTag falls in here - case _ => abort("could not handle attribute argument: " + c) - } - - val cnst: Constant = nvPair._2 - if (cnst.tag == ArrayTag) { - buf.put(0x1d.toByte) - emitType(cnst.arrayValue(0)) // FIXME: will crash if array length = 0 - } else if (cnst.tag == EnumTag) { - buf.put(0x55.toByte) - // TODO: put a SerString (don't know what exactly, names of the enums somehow..) - } else { - buf.put(0x51.toByte) - emitType(cnst) - } - - emitSerString(nvPair._1.toString) - emitConst(nvPair._2) - } - - val length = buf.position() - buf.array().slice(0, length) - } */ - - def writeAssembly() { - if (entryPoint != null) { - assert(entryPoint.enclClass.isModuleClass, entryPoint.enclClass) - val mainMethod = methods(entryPoint) - val stringArrayTypes: Array[MsilType] = Array(MSTRING_ARRAY) - val globalMain = mmodule.DefineGlobalMethod( - "Main", MethodAttributes.Public | MethodAttributes.Static, - MVOID, stringArrayTypes) - globalMain.DefineParameter(0, ParameterAttributes.None, "args") - massembly.SetEntryPoint(globalMain) - val code = globalMain.GetILGenerator() - val moduleField = getModuleInstanceField(entryPoint.enclClass) - code.Emit(OpCodes.Ldsfld, moduleField) - code.Emit(OpCodes.Ldarg_0) - code.Emit(OpCodes.Callvirt, mainMethod) - code.Emit(OpCodes.Ret) - } - createTypes() - var outDirName: String = null - try { - if (settings.Ygenjavap.isDefault) { // we reuse the JVM-sounding setting because it's conceptually similar - outDirName = outDir.getPath() - massembly.Save(outDirName + "\\" + assemName + ".msil") /* use SingleFileILPrinterVisitor */ - } else { - outDirName = srcPath.getPath() - massembly.Save(settings.Ygenjavap.value, outDirName) /* use MultipleFilesILPrinterVisitor */ - } - } catch { - case e:IOException => abort("Could not write to " + outDirName + ": " + e.getMessage()) - } - } - - private def createTypes() { - for (sym <- classes.keys) { - val iclass = classes(sym) - val tBuilder = types(sym).asInstanceOf[TypeBuilder] - - debuglog("Calling CreatType for " + sym + ", " + tBuilder.toString) - - tBuilder.CreateType() - tBuilder.setSourceFilepath(iclass.cunit.source.file.path) - } - } - - private[GenMSIL] def ilasmFileName(iclass: IClass) : String = { - // method.sourceFile contains just the filename - iclass.cunit.source.file.toString.replace("\\", "\\\\") - } - - private[GenMSIL] def genClass(iclass: IClass) { - val sym = iclass.symbol - debuglog("Generating class " + sym + " flags: " + sym.flagString) - clasz = iclass - - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - if (isCloneable(sym)) { - // FIXME: why there's no nme.clone_ ? - // "Clone": if the code is non-portable, "Clone" is defined, not "clone" - // TODO: improve condition (should override AnyRef.clone) - if (iclass.methods.forall(m => { - !((m.symbol.name.toString != "clone" || m.symbol.name.toString != "Clone") && - m.symbol.tpe.paramTypes.length != 0) - })) { - debuglog("auto-generating cloneable method for " + sym) - val attrs: Short = (MethodAttributes.Public | MethodAttributes.Virtual | - MethodAttributes.HideBySig).toShort - val cloneMethod = tBuilder.DefineMethod("Clone", attrs, MOBJECT, - MsilType.EmptyTypes) - val clCode = cloneMethod.GetILGenerator() - clCode.Emit(OpCodes.Ldarg_0) - clCode.Emit(OpCodes.Call, MEMBERWISE_CLONE) - clCode.Emit(OpCodes.Ret) - } - } - - val line = sym.pos.line - tBuilder.setPosition(line, ilasmFileName(iclass)) - - if (isTopLevelModule(sym)) { - if (sym.companionClass == NoSymbol) - generateMirrorClass(sym) - else - log("No mirror class for module with linked class: " + - sym.fullName) - } - - addSymtabAttribute(sym, tBuilder) - addAttributes(tBuilder, sym.annotations) - - if (iclass.symbol != definitions.ArrayClass) - iclass.methods foreach genMethod - - } //genClass - - - private def genMethod(m: IMethod) { - debuglog("Generating method " + m.symbol + " flags: " + m.symbol.flagString + - " owner: " + m.symbol.owner) - method = m - localBuilders.clear - computeLocalVarsIndex(m) - - if (m.symbol.isClassConstructor) { - mcode = constructors(m.symbol).asInstanceOf[ConstructorBuilder].GetILGenerator() - } else { - val mBuilder = methods(m.symbol).asInstanceOf[MethodBuilder] - if (!mBuilder.IsAbstract()) - try { - mcode = mBuilder.GetILGenerator() - } catch { - case e: Exception => - java.lang.System.out.println("m.symbol = " + m.symbol.flagString + " " + m.symbol) - java.lang.System.out.println("m.symbol.owner = " + m.symbol.owner.flagString + " " + m.symbol.owner) - java.lang.System.out.println("mBuilder = " + mBuilder) - java.lang.System.out.println("mBuilder.DeclaringType = " + - TypeAttributes.toString(mBuilder.DeclaringType.Attributes) + - "::" + mBuilder.DeclaringType) - throw e - } - else - mcode = null - } - - if (mcode != null) { - for (local <- m.locals ; if !(m.params contains local)) { - debuglog("add local var: " + local + ", of kind " + local.kind) - val t: MsilType = msilType(local.kind) - val localBuilder = mcode.DeclareLocal(t) - localBuilder.SetLocalSymInfo(msilName(local.sym)) - localBuilders(local) = localBuilder - } - genCode(m) - } - - } - - /** Special linearizer for methods with at least one exception handler. This - * linearizer brings all basic blocks in the right order so that nested - * try-catch and try-finally blocks can be emitted. - */ - val msilLinearizer = new MSILLinearizer() - - val labels = mutable.HashMap[BasicBlock, Label]() - - /* when emitting .line, it's enough to include the full filename just once per method, thus reducing filesize. - * this scheme relies on the fact that the entry block is emitted first. */ - var dbFilenameSeen = false - - def genCode(m: IMethod) { - - def makeLabels(blocks: List[BasicBlock]) = { - debuglog("Making labels for: " + method) - for (bb <- blocks) labels(bb) = mcode.DefineLabel() - } - - labels.clear - - var linearization = if(!m.exh.isEmpty) msilLinearizer.linearize(m) - else linearizer.linearize(m) - - if (!m.exh.isEmpty) - linearization = computeExceptionMaps(linearization, m) - - makeLabels(linearization) - - // debug val blocksInM = m.code.blocks.toList.sortBy(bb => bb.label) - // debug val blocksInL = linearization.sortBy(bb => bb.label) - // debug val MButNotL = (blocksInM.toSet) diff (blocksInL.toSet) // if non-empty, a jump to B fails to find a label for B (case CJUMP, case CZJUMP) - // debug if(!MButNotL.isEmpty) { } - - dbFilenameSeen = false - genBlocks(linearization) - - // RETURN inside exception blocks are replaced by Leave. The target of the - // leave is a `Ret` outside any exception block (generated here). - if (handlerReturnMethod == m) { - mcode.MarkLabel(handlerReturnLabel) - if (handlerReturnKind != UNIT) - mcode.Emit(OpCodes.Ldloc, handlerReturnLocal) - mcode.Emit(OpCodes.Ret) - } - - beginExBlock.clear() - beginCatchBlock.clear() - endExBlock.clear() - endFinallyLabels.clear() - } - - def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) { - blocks match { - case Nil => () - case x :: Nil => genBlock(x, prev = previous, next = null) - case x :: y :: ys => genBlock(x, prev = previous, next = y); genBlocks(y :: ys, previous = x) - } - } - - // the try blocks starting at a certain BasicBlock - val beginExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() - - // the catch blocks starting / endling at a certain BasicBlock - val beginCatchBlock = mutable.HashMap[BasicBlock, ExceptionHandler]() - val endExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() - - /** When emitting the code (genBlock), the number of currently active try / catch - * blocks. When seeing a `RETURN` inside a try / catch, we need to - * - store the result in a local (if it's not UNIT) - * - emit `Leave handlerReturnLabel` instead of the Return - * - emit code at the end: load the local and return its value - */ - val currentHandlers = new mutable.Stack[ExceptionHandler] - // The IMethod the Local/Label/Kind below belong to - var handlerReturnMethod: IMethod = _ - // Stores the result when returning inside an exception block - var handlerReturnLocal: LocalBuilder = _ - // Label for a return instruction outside any exception block - var handlerReturnLabel: Label = _ - // The result kind. - var handlerReturnKind: TypeKind = _ - def returnFromHandler(kind: TypeKind): (LocalBuilder, Label) = { - if (handlerReturnMethod != method) { - handlerReturnMethod = method - if (kind != UNIT) { - handlerReturnLocal = mcode.DeclareLocal(msilType(kind)) - handlerReturnLocal.SetLocalSymInfo("$handlerReturn") - } - handlerReturnLabel = mcode.DefineLabel() - handlerReturnKind = kind - } - (handlerReturnLocal, handlerReturnLabel) - } - - /** For try/catch nested inside a finally, we can't use `Leave OutsideFinally`, the - * Leave target has to be inside the finally (and it has to be the `endfinally` instruction). - * So for every finalizer, we have a label which marks the place of the `endfinally`, - * nested try/catch blocks will leave there. - */ - val endFinallyLabels = mutable.HashMap[ExceptionHandler, Label]() - - /** Computes which blocks are the beginning / end of a try or catch block */ - private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = { - val visitedBlocks = new mutable.HashSet[BasicBlock]() - - // handlers which have not been introduced so far - var openHandlers = m.exh - - - /** Example - * try { - * try { - * // *1* - * } catch { - * case h1 => - * } - * } catch { - * case h2 => - * case h3 => - * try { - * - * } catch { - * case h4 => // *2* - * case h5 => - * } - * } - */ - - // Stack of nested try blocks. Each bloc has a List of ExceptionHandler (multiple - // catch statements). Example *1*: Stack(List(h2, h3), List(h1)) - val currentTryHandlers = new mutable.Stack[List[ExceptionHandler]]() - - // Stack of nested catch blocks. The head of the list is the current catch block. The - // tail is all following catch blocks. Example *2*: Stack(List(h3), List(h4, h5)) - val currentCatchHandlers = new mutable.Stack[List[ExceptionHandler]]() - - for (b <- blocks) { - - // are we past the current catch blocks? - def endHandlers(): List[ExceptionHandler] = { - var res: List[ExceptionHandler] = Nil - if (!currentCatchHandlers.isEmpty) { - val handler = currentCatchHandlers.top.head - if (!handler.blocks.contains(b)) { - // all blocks of the handler are either visited, or not part of the linearization (i.e. dead) - assert(handler.blocks.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), - "Bad linearization of basic blocks inside catch. Found block not part of the handler\n"+ - b.fullString +"\nwhile in catch-part of\n"+ handler) - - val rest = currentCatchHandlers.pop.tail - if (rest.isEmpty) { - // all catch blocks of that exception handler are covered - res = handler :: endHandlers() - } else { - // there are more catch blocks for that try (handlers covering the same) - currentCatchHandlers.push(rest) - beginCatchBlock(b) = rest.head - } - } - } - res - } - val end = endHandlers() - if (!end.isEmpty) endExBlock(b) = end - - // are we past the current try block? - if (!currentTryHandlers.isEmpty) { - val handler = currentTryHandlers.top.head - if (!handler.covers(b)) { - // all of the covered blocks are visited, or not part of the linearization - assert(handler.covered.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), - "Bad linearization of basic blocks inside try. Found non-covered block\n"+ - b.fullString +"\nwhile in try-part of\n"+ handler) - - assert(handler.startBlock == b, - "Bad linearization of basic blocks. The entry block of a catch does not directly follow the try\n"+ - b.fullString +"\n"+ handler) - - val handlers = currentTryHandlers.pop - currentCatchHandlers.push(handlers) - beginCatchBlock(b) = handler - } - } - - // are there try blocks starting at b? - val (newHandlers, stillOpen) = openHandlers.partition(_.covers(b)) - openHandlers = stillOpen - - val newHandlersBySize = newHandlers.groupBy(_.covered.size) - // big handlers first, smaller ones are nested inside the try of the big one - // (checked by the assertions below) - val sizes = newHandlersBySize.keys.toList.sortWith(_ > _) - - val beginHandlers = new mutable.ListBuffer[ExceptionHandler] - for (s <- sizes) { - val sHandlers = newHandlersBySize(s) - for (h <- sHandlers) { - assert(h.covered == sHandlers.head.covered, - "bad nesting of exception handlers. same size, but not covering same blocks\n"+ - h +"\n"+ sHandlers.head) - assert(h.resultKind == sHandlers.head.resultKind, - "bad nesting of exception handlers. same size, but the same resultKind\n"+ - h +"\n"+ sHandlers.head) - } - for (bigger <- beginHandlers; h <- sHandlers) { - assert(h.covered.subsetOf(bigger.covered), - "bad nesting of exception handlers. try blocks of smaller handler are not nested in bigger one.\n"+ - h +"\n"+ bigger) - assert(h.blocks.toSet.subsetOf(bigger.covered), - "bad nesting of exception handlers. catch blocks of smaller handler are not nested in bigger one.\n"+ - h +"\n"+ bigger) - } - beginHandlers += sHandlers.head - currentTryHandlers.push(sHandlers) - } - beginExBlock(b) = beginHandlers.toList - visitedBlocks += b - } - - // if there handlers left (i.e. handlers covering nothing, or a - // non-existent (dead) block), remove their catch-blocks. - val liveBlocks = if (openHandlers.isEmpty) blocks else { - blocks.filter(b => openHandlers.forall(h => !h.blocks.contains(b))) - } - - /** There might be open handlers, but no more blocks. happens when try/catch end - * with `throw` or `return` - * def foo() { try { .. throw } catch { _ => .. throw } } - * - * In this case we need some code after the catch block for the auto-generated - * `leave` instruction. So we're adding a (dead) `throw new Exception`. - */ - val rest = currentCatchHandlers.map(handlers => { - assert(handlers.length == 1, handlers) - handlers.head - }).toList - - if (rest.isEmpty) { - liveBlocks - } else { - val b = m.code.newBlock - b.emit(Seq( - NEW(REFERENCE(definitions.ThrowableClass)), - DUP(REFERENCE(definitions.ObjectClass)), - CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)), - THROW(definitions.ThrowableClass) - )) - b.close - endExBlock(b) = rest - liveBlocks ::: List(b) - } - } - - /** - * @param block the BasicBlock to emit code for - * @param next the following BasicBlock, `null` if `block` is the last one - */ - def genBlock(block: BasicBlock, prev: BasicBlock, next: BasicBlock) { - - def loadLocalOrAddress(local: Local, msg : String , loadAddr : Boolean) { - debuglog(msg + " for " + local) - val isArg = local.arg - val i = local.index - if (isArg) - loadArg(mcode, loadAddr)(i) - else - loadLocal(i, local, mcode, loadAddr) - } - - def loadFieldOrAddress(field: Symbol, isStatic: Boolean, msg: String, loadAddr : Boolean) { - debuglog(msg + " with owner: " + field.owner + - " flags: " + field.owner.flagString) - val fieldInfo = fields.get(field) match { - case Some(fInfo) => fInfo - case None => - val fInfo = getType(field.owner).GetField(msilName(field)) - fields(field) = fInfo - fInfo - } - if (fieldInfo.IsVolatile) { - mcode.Emit(OpCodes.Volatile) - } - if (!fieldInfo.IsLiteral) { - if (loadAddr) { - mcode.Emit(if (isStatic) OpCodes.Ldsflda else OpCodes.Ldflda, fieldInfo) - } else { - mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo) - } - } else { - assert(!loadAddr, "can't take AddressOf a literal field (not even with readonly. prefix) because no memory was allocated to such field ...") - // TODO the above can be overcome by loading the value, boxing, and finally unboxing. An address to a copy of the raw value will be on the stack. - /* We perform `field inlining' as required by CLR. - * Emit as for a CONSTANT ICode stmt, with the twist that the constant value is available - * as a java.lang.Object and its .NET type allows constant initialization in CLR, i.e. that type - * is one of I1, I2, I4, I8, R4, R8, CHAR, BOOLEAN, STRING, or CLASS (in this last case, - * only accepting nullref as value). See Table 9-1 in Lidin's book on ILAsm. */ - val value = fieldInfo.getValue() - if (value == null) { - mcode.Emit(OpCodes.Ldnull) - } else { - val typ = if (fieldInfo.FieldType.IsEnum) fieldInfo.FieldType.getUnderlyingType - else fieldInfo.FieldType - if (typ == clrTypes.STRING) { - mcode.Emit(OpCodes.Ldstr, value.asInstanceOf[String]) - } else if (typ == clrTypes.BOOLEAN) { - mcode.Emit(if (value.asInstanceOf[Boolean]) OpCodes.Ldc_I4_1 - else OpCodes.Ldc_I4_0) - } else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE) { - loadI4(value.asInstanceOf[Byte], mcode) - } else if (typ == clrTypes.SHORT || typ == clrTypes.USHORT) { - loadI4(value.asInstanceOf[Int], mcode) - } else if (typ == clrTypes.CHAR) { - loadI4(value.asInstanceOf[Char], mcode) - } else if (typ == clrTypes.INT || typ == clrTypes.UINT) { - loadI4(value.asInstanceOf[Int], mcode) - } else if (typ == clrTypes.LONG || typ == clrTypes.ULONG) { - mcode.Emit(OpCodes.Ldc_I8, value.asInstanceOf[Long]) - } else if (typ == clrTypes.FLOAT) { - mcode.Emit(OpCodes.Ldc_R4, value.asInstanceOf[Float]) - } else if (typ == clrTypes.DOUBLE) { - mcode.Emit(OpCodes.Ldc_R8, value.asInstanceOf[Double]) - } else { - /* TODO one more case is described in Partition II, 16.2: bytearray(...) */ - abort("Unknown type for static literal field: " + fieldInfo) - } - } - } - } - - /** Creating objects works differently on .NET. On the JVM - * - NEW(type) => reference on Stack - * - DUP, load arguments, CALL_METHOD(constructor) - * - * On .NET, the NEW and DUP are ignored, but we emit a special method call - * - load arguments - * - NewObj(constructor) => reference on stack - * - * This variable tells whether the previous instruction was a NEW, - * we expect a DUP which is not emitted. */ - var previousWasNEW = false - - var lastLineNr: Int = 0 - var lastPos: Position = NoPosition - - - // EndExceptionBlock must happen before MarkLabel because it adds the - // Leave instruction. Otherwise, labels(block) points to the Leave - // (inside the catch) instead of the instruction afterwards. - for (handlers <- endExBlock.get(block); exh <- handlers) { - currentHandlers.pop() - for (l <- endFinallyLabels.get(exh)) - mcode.MarkLabel(l) - mcode.EndExceptionBlock() - } - - mcode.MarkLabel(labels(block)) - debuglog("Generating code for block: " + block) - - for (handler <- beginCatchBlock.get(block)) { - if (!currentHandlers.isEmpty && currentHandlers.top.covered == handler.covered) { - currentHandlers.pop() - currentHandlers.push(handler) - } - if (handler.cls == NoSymbol) { - // `finally` blocks are represented the same as `catch`, but with no catch-type - mcode.BeginFinallyBlock() - } else { - val t = getType(handler.cls) - mcode.BeginCatchBlock(t) - } - } - for (handlers <- beginExBlock.get(block); exh <- handlers) { - currentHandlers.push(exh) - mcode.BeginExceptionBlock() - } - - for (instr <- block) { - try { - val currentLineNr = instr.pos.line - val skip = if(instr.pos.isRange) instr.pos.sameRange(lastPos) else (currentLineNr == lastLineNr); - if(!skip || !dbFilenameSeen) { - val fileName = if(dbFilenameSeen) "" else {dbFilenameSeen = true; ilasmFileName(clasz)}; - if(instr.pos.isRange) { - val startLine = instr.pos.focusStart.line - val endLine = instr.pos.focusEnd.line - val startCol = instr.pos.focusStart.column - val endCol = instr.pos.focusEnd.column - mcode.setPosition(startLine, endLine, startCol, endCol, fileName) - } else { - mcode.setPosition(instr.pos.line, fileName) - } - lastLineNr = currentLineNr - lastPos = instr.pos - } - } catch { case _: UnsupportedOperationException => () } - - if (previousWasNEW) - assert(instr.isInstanceOf[DUP], block) - - instr match { - case THIS(clasz) => - mcode.Emit(OpCodes.Ldarg_0) - - case CONSTANT(const) => - const.tag match { - case UnitTag => () - case BooleanTag => mcode.Emit(if (const.booleanValue) OpCodes.Ldc_I4_1 - else OpCodes.Ldc_I4_0) - case ByteTag => loadI4(const.byteValue, mcode) - case ShortTag => loadI4(const.shortValue, mcode) - case CharTag => loadI4(const.charValue, mcode) - case IntTag => loadI4(const.intValue, mcode) - case LongTag => mcode.Emit(OpCodes.Ldc_I8, const.longValue) - case FloatTag => mcode.Emit(OpCodes.Ldc_R4, const.floatValue) - case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue) - case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue) - case NullTag => mcode.Emit(OpCodes.Ldnull) - case ClazzTag => - mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue)) - mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE) - case _ => abort("Unknown constant value: " + const) - } - - case LOAD_ARRAY_ITEM(kind) => - (kind: @unchecked) match { - case BOOL => mcode.Emit(OpCodes.Ldelem_I1) - case BYTE => mcode.Emit(OpCodes.Ldelem_I1) // I1 for System.SByte, i.e. a scala.Byte - case SHORT => mcode.Emit(OpCodes.Ldelem_I2) - case CHAR => mcode.Emit(OpCodes.Ldelem_U2) - case INT => mcode.Emit(OpCodes.Ldelem_I4) - case LONG => mcode.Emit(OpCodes.Ldelem_I8) - case FLOAT => mcode.Emit(OpCodes.Ldelem_R4) - case DOUBLE => mcode.Emit(OpCodes.Ldelem_R8) - case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref) - case ARRAY(elem) => mcode.Emit(OpCodes.Ldelem_Ref) - - // case UNIT is not possible: an Array[Unit] will be an - // Array[scala.runtime.BoxedUnit] (-> case REFERENCE) - } - - case LOAD_LOCAL(local) => loadLocalOrAddress(local, "load_local", false) - - case CIL_LOAD_LOCAL_ADDRESS(local) => loadLocalOrAddress(local, "cil_load_local_address", true) - - case LOAD_FIELD(field, isStatic) => loadFieldOrAddress(field, isStatic, "load_field", false) - - case CIL_LOAD_FIELD_ADDRESS(field, isStatic) => loadFieldOrAddress(field, isStatic, "cil_load_field_address", true) - - case CIL_LOAD_ARRAY_ITEM_ADDRESS(kind) => mcode.Emit(OpCodes.Ldelema, msilType(kind)) - - case CIL_NEWOBJ(msym) => - assert(msym.isClassConstructor) - val constructorInfo: ConstructorInfo = getConstructor(msym) - mcode.Emit(OpCodes.Newobj, constructorInfo) - - case LOAD_MODULE(module) => - debuglog("Generating LOAD_MODULE for: " + showsym(module)) - mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module)) - - case STORE_ARRAY_ITEM(kind) => - (kind: @unchecked) match { - case BOOL => mcode.Emit(OpCodes.Stelem_I1) - case BYTE => mcode.Emit(OpCodes.Stelem_I1) - case SHORT => mcode.Emit(OpCodes.Stelem_I2) - case CHAR => mcode.Emit(OpCodes.Stelem_I2) - case INT => mcode.Emit(OpCodes.Stelem_I4) - case LONG => mcode.Emit(OpCodes.Stelem_I8) - case FLOAT => mcode.Emit(OpCodes.Stelem_R4) - case DOUBLE => mcode.Emit(OpCodes.Stelem_R8) - case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref) - case ARRAY(elem) => mcode.Emit(OpCodes.Stelem_Ref) // @TODO: test this! (occurs when calling a Array[Object]* vararg param method) - - // case UNIT not possible (see comment at LOAD_ARRAY_ITEM) - } - - case STORE_LOCAL(local) => - val isArg = local.arg - val i = local.index - debuglog("store_local for " + local + ", index " + i) - - // there are some locals defined by the compiler that - // are isArg and are need to be stored. - if (isArg) { - if (i >= -128 && i <= 127) - mcode.Emit(OpCodes.Starg_S, i) - else - mcode.Emit(OpCodes.Starg, i) - } else { - i match { - case 0 => mcode.Emit(OpCodes.Stloc_0) - case 1 => mcode.Emit(OpCodes.Stloc_1) - case 2 => mcode.Emit(OpCodes.Stloc_2) - case 3 => mcode.Emit(OpCodes.Stloc_3) - case _ => - if (i >= -128 && i <= 127) - mcode.Emit(OpCodes.Stloc_S, localBuilders(local)) - else - mcode.Emit(OpCodes.Stloc, localBuilders(local)) - } - } - - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - mcode.Emit(OpCodes.Starg_S, 0) - - case STORE_FIELD(field, isStatic) => - val fieldInfo = fields.get(field) match { - case Some(fInfo) => fInfo - case None => - val fInfo = getType(field.owner).GetField(msilName(field)) - fields(field) = fInfo - fInfo - } - mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo) - - case CALL_PRIMITIVE(primitive) => - genPrimitive(primitive, instr.pos) - - case CALL_METHOD(msym, style) => - if (msym.isClassConstructor) { - val constructorInfo: ConstructorInfo = getConstructor(msym) - (style: @unchecked) match { - // normal constructor calls are Static.. - case Static(_) => - if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner) - // we're generating a constructor (method: IMethod is a constructor), and we're - // calling another constructor of the same class. - - // @LUC TODO: this can probably break, namely when having: class A { def this() { new A() } } - // instead, we should instruct the CALL_METHOD with additional information, know whether it's - // an instance creation constructor call or not. - mcode.Emit(OpCodes.Call, constructorInfo) - else - mcode.Emit(OpCodes.Newobj, constructorInfo) - case SuperCall(_) => - mcode.Emit(OpCodes.Call, constructorInfo) - if (isStaticModule(clasz.symbol) && - notInitializedModules.contains(clasz.symbol) && - method.symbol.isClassConstructor) - { - notInitializedModules -= clasz.symbol - mcode.Emit(OpCodes.Ldarg_0) - mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol)) - } - } - - } else { - - var doEmit = true - getTypeOpt(msym.owner) match { - case Some(typ) if (typ.IsEnum) => { - def negBool() = { - mcode.Emit(OpCodes.Ldc_I4_0) - mcode.Emit(OpCodes.Ceq) - } - doEmit = false - val name = msym.name - if (name eq nme.EQ) { mcode.Emit(OpCodes.Ceq) } - else if (name eq nme.NE) { mcode.Emit(OpCodes.Ceq); negBool } - else if (name eq nme.LT) { mcode.Emit(OpCodes.Clt) } - else if (name eq nme.LE) { mcode.Emit(OpCodes.Cgt); negBool } - else if (name eq nme.GT) { mcode.Emit(OpCodes.Cgt) } - else if (name eq nme.GE) { mcode.Emit(OpCodes.Clt); negBool } - else if (name eq nme.OR) { mcode.Emit(OpCodes.Or) } - else if (name eq nme.AND) { mcode.Emit(OpCodes.And) } - else if (name eq nme.XOR) { mcode.Emit(OpCodes.Xor) } - else - doEmit = true - } - case _ => () - } - - // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType - val (isDelegateView, paramType, resType) = enteringTyper { - msym.tpe match { - case MethodType(params, resultType) - if (params.length == 1 && msym.name == nme.view_) => - val paramType = params(0).tpe - val isDel = definitions.isCorrespondingDelegate(resultType, paramType) - (isDel, paramType, resultType) - case _ => (false, null, null) - } - } - if (doEmit && isDelegateView) { - doEmit = false - createDelegateCaller(paramType, resType) - } - - if (doEmit && - (msym.name == nme.PLUS || msym.name == nme.MINUS) - && clrTypes.isDelegateType(msilType(msym.owner.tpe))) - { - doEmit = false - val methodInfo: MethodInfo = getMethod(msym) - // call it as a static method, even if the compiler (symbol) thinks it's virtual - mcode.Emit(OpCodes.Call, methodInfo) - mcode.Emit(OpCodes.Castclass, msilType(msym.owner.tpe)) - } - - if (doEmit && definitions.Delegate_scalaCallers.contains(msym)) { - doEmit = false - val methodSym: Symbol = definitions.Delegate_scalaCallerTargets(msym) - val delegateType: Type = msym.tpe match { - case MethodType(_, retType) => retType - case _ => abort("not a method type: " + msym.tpe) - } - val methodInfo: MethodInfo = getMethod(methodSym) - val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) - if (methodSym.isStatic) { - mcode.Emit(OpCodes.Ldftn, methodInfo) - } else { - mcode.Emit(OpCodes.Dup) - mcode.Emit(OpCodes.Ldvirtftn, methodInfo) - } - mcode.Emit(OpCodes.Newobj, delegCtor) - } - - if (doEmit) { - val methodInfo: MethodInfo = getMethod(msym) - (style: @unchecked) match { - case SuperCall(_) => - mcode.Emit(OpCodes.Call, methodInfo) - case Dynamic => - // methodInfo.DeclaringType is null for global methods - val isValuetypeMethod = (methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType) - val isValuetypeVirtualMethod = isValuetypeMethod && (methodInfo.IsVirtual) - if (dynToStatMapped(msym)) { - mcode.Emit(OpCodes.Call, methodInfo) - } else if (isValuetypeVirtualMethod) { - mcode.Emit(OpCodes.Constrained, methodInfo.DeclaringType) - mcode.Emit(OpCodes.Callvirt, methodInfo) - } else if (isValuetypeMethod) { - // otherwise error "Callvirt on a value type method" ensues - mcode.Emit(OpCodes.Call, methodInfo) - } else { - mcode.Emit(OpCodes.Callvirt, methodInfo) - } - case Static(_) => - if(methodInfo.IsVirtual && !mcode.Ldarg0WasJustEmitted) { - mcode.Emit(OpCodes.Callvirt, methodInfo) - } else mcode.Emit(OpCodes.Call, methodInfo) - } - } - } - - case BOX(boxType) => - emitBox(mcode, boxType) - - case UNBOX(boxType) => - emitUnbox(mcode, boxType) - - case CIL_UNBOX(boxType) => - mcode.Emit(OpCodes.Unbox, msilType(boxType)) - - case CIL_INITOBJ(valueType) => - mcode.Emit(OpCodes.Initobj, msilType(valueType)) - - case NEW(REFERENCE(cls)) => - // the next instruction must be a DUP, see comment on `var previousWasNEW` - previousWasNEW = true - - // works also for arrays and reference-types - case CREATE_ARRAY(elem, dims) => - // TODO: handle multi dimensional arrays - assert(dims == 1, "Can't handle multi dimensional arrays") - mcode.Emit(OpCodes.Newarr, msilType(elem)) - - // works for arrays and reference-types - case IS_INSTANCE(tpe) => - mcode.Emit(OpCodes.Isinst, msilType(tpe)) - mcode.Emit(OpCodes.Ldnull) - mcode.Emit(OpCodes.Ceq) - mcode.Emit(OpCodes.Ldc_I4_0) - mcode.Emit(OpCodes.Ceq) - - // works for arrays and reference-types - // part from the scala reference: "S <: T does not imply - // Array[S] <: Array[T] in Scala. However, it is possible - // to cast an array of S to an array of T if such a cast - // is permitted in the host environment." - case CHECK_CAST(tpknd) => - val tMSIL = msilType(tpknd) - mcode.Emit(OpCodes.Castclass, tMSIL) - - // no SWITCH is generated when there's - // - a default case ("case _ => ...") in the matching expr - // - OR is used ("case 1 | 2 => ...") - case SWITCH(tags, branches) => - // tags is List[List[Int]]; a list of integers for every label. - // if the int on stack is 4, and 4 is in the second list => jump - // to second label - // branches is List[BasicBlock] - // the labels to jump to (the last one is the default one) - - val switchLocal = mcode.DeclareLocal(MINT) - // several switch variables will appear with the same name in the - // assembly code, but this makes no truble - switchLocal.SetLocalSymInfo("$switch_var") - - mcode.Emit(OpCodes.Stloc, switchLocal) - var i = 0 - for (l <- tags) { - val targetLabel = labels(branches(i)) - for (i <- l) { - mcode.Emit(OpCodes.Ldloc, switchLocal) - loadI4(i, mcode) - mcode.Emit(OpCodes.Beq, targetLabel) - } - i += 1 - } - val defaultTarget = labels(branches(i)) - if (next != branches(i)) - mcode.Emit(OpCodes.Br, defaultTarget) - - case JUMP(whereto) => - val (leaveHandler, leaveFinally, lfTarget) = leavesHandler(block, whereto) - if (leaveHandler) { - if (leaveFinally) { - if (lfTarget.isDefined) mcode.Emit(OpCodes.Leave, lfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(whereto)) - } else if (next != whereto) - mcode.Emit(OpCodes.Br, labels(whereto)) - - case CJUMP(success, failure, cond, kind) => - // cond is TestOp (see Primitives.scala), and can take - // values EQ, NE, LT, GE LE, GT - // kind is TypeKind - val isFloat = kind == FLOAT || kind == DOUBLE - val emit = (c: TestOp, l: Label) => emitBr(c, l, isFloat) - emitCondBr(block, cond, success, failure, next, emit) - - case CZJUMP(success, failure, cond, kind) => - emitCondBr(block, cond, success, failure, next, emitBrBool(_, _)) - - case RETURN(kind) => - if (currentHandlers.isEmpty) - mcode.Emit(OpCodes.Ret) - else { - val (local, label) = returnFromHandler(kind) - if (kind != UNIT) - mcode.Emit(OpCodes.Stloc, local) - mcode.Emit(OpCodes.Leave, label) - } - - case THROW(_) => - mcode.Emit(OpCodes.Throw) - - case DROP(kind) => - mcode.Emit(OpCodes.Pop) - - case DUP(kind) => - // see comment on `var previousWasNEW` - if (!previousWasNEW) - mcode.Emit(OpCodes.Dup) - else - previousWasNEW = false - - case MONITOR_ENTER() => - mcode.Emit(OpCodes.Call, MMONITOR_ENTER) - - case MONITOR_EXIT() => - mcode.Emit(OpCodes.Call, MMONITOR_EXIT) - - case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) => - () - } - - } // end for (instr <- b) { .. } - } // end genBlock - - def genPrimitive(primitive: Primitive, pos: Position) { - primitive match { - case Negation(kind) => - kind match { - // CHECK: is ist possible to get this for BOOL? in this case, verify. - case BOOL | BYTE | CHAR | SHORT | INT | LONG | FLOAT | DOUBLE => - mcode.Emit(OpCodes.Neg) - - case _ => abort("Impossible to negate a " + kind) - } - - case Arithmetic(op, kind) => - op match { - case ADD => mcode.Emit(OpCodes.Add) - case SUB => mcode.Emit(OpCodes.Sub) - case MUL => mcode.Emit(OpCodes.Mul) - case DIV => mcode.Emit(OpCodes.Div) - case REM => mcode.Emit(OpCodes.Rem) - case NOT => mcode.Emit(OpCodes.Not) //bitwise complement (one's complement) - case _ => abort("Unknown arithmetic primitive " + primitive ) - } - - case Logical(op, kind) => op match { - case AND => mcode.Emit(OpCodes.And) - case OR => mcode.Emit(OpCodes.Or) - case XOR => mcode.Emit(OpCodes.Xor) - } - - case Shift(op, kind) => op match { - case LSL => mcode.Emit(OpCodes.Shl) - case ASR => mcode.Emit(OpCodes.Shr) - case LSR => mcode.Emit(OpCodes.Shr_Un) - } - - case Conversion(src, dst) => - debuglog("Converting from: " + src + " to: " + dst) - - dst match { - case BYTE => mcode.Emit(OpCodes.Conv_I1) // I1 for System.SByte, i.e. a scala.Byte - case SHORT => mcode.Emit(OpCodes.Conv_I2) - case CHAR => mcode.Emit(OpCodes.Conv_U2) - case INT => mcode.Emit(OpCodes.Conv_I4) - case LONG => mcode.Emit(OpCodes.Conv_I8) - case FLOAT => mcode.Emit(OpCodes.Conv_R4) - case DOUBLE => mcode.Emit(OpCodes.Conv_R8) - case _ => - Console.println("Illegal conversion at: " + clasz + - " at: " + pos.source + ":" + pos.line) - } - - case ArrayLength(_) => - mcode.Emit(OpCodes.Ldlen) - - case StartConcat => - mcode.Emit(OpCodes.Newobj, MSTRING_BUILDER_CONSTR) - - - case StringConcat(el) => - val elemType : MsilType = el match { - case REFERENCE(_) | ARRAY(_) => MOBJECT - case _ => msilType(el) - } - - val argTypes:Array[MsilType] = Array(elemType) - val stringBuilderAppend = MSTRING_BUILDER.GetMethod("Append", argTypes ) - mcode.Emit(OpCodes.Callvirt, stringBuilderAppend) - - case EndConcat => - mcode.Emit(OpCodes.Callvirt, MSTRING_BUILDER_TOSTRING) - - case _ => - abort("Unimplemented primitive " + primitive) - } - } // end genPrimitive - - - ////////////////////// loading /////////////////////// - - def loadI4(value: Int, code: ILGenerator): Unit = value match { - case -1 => code.Emit(OpCodes.Ldc_I4_M1) - case 0 => code.Emit(OpCodes.Ldc_I4_0) - case 1 => code.Emit(OpCodes.Ldc_I4_1) - case 2 => code.Emit(OpCodes.Ldc_I4_2) - case 3 => code.Emit(OpCodes.Ldc_I4_3) - case 4 => code.Emit(OpCodes.Ldc_I4_4) - case 5 => code.Emit(OpCodes.Ldc_I4_5) - case 6 => code.Emit(OpCodes.Ldc_I4_6) - case 7 => code.Emit(OpCodes.Ldc_I4_7) - case 8 => code.Emit(OpCodes.Ldc_I4_8) - case _ => - if (value >= -128 && value <= 127) - code.Emit(OpCodes.Ldc_I4_S, value) - else - code.Emit(OpCodes.Ldc_I4, value) - } - - def loadArg(code: ILGenerator, loadAddr: Boolean)(i: Int) = - if (loadAddr) { - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldarga_S, i) - else - code.Emit(OpCodes.Ldarga, i) - } else { - i match { - case 0 => code.Emit(OpCodes.Ldarg_0) - case 1 => code.Emit(OpCodes.Ldarg_1) - case 2 => code.Emit(OpCodes.Ldarg_2) - case 3 => code.Emit(OpCodes.Ldarg_3) - case _ => - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldarg_S, i) - else - code.Emit(OpCodes.Ldarg, i) - } - } - - def loadLocal(i: Int, local: Local, code: ILGenerator, loadAddr: Boolean) = - if (loadAddr) { - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldloca_S, localBuilders(local)) - else - code.Emit(OpCodes.Ldloca, localBuilders(local)) - } else { - i match { - case 0 => code.Emit(OpCodes.Ldloc_0) - case 1 => code.Emit(OpCodes.Ldloc_1) - case 2 => code.Emit(OpCodes.Ldloc_2) - case 3 => code.Emit(OpCodes.Ldloc_3) - case _ => - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldloc_S, localBuilders(local)) - else - code.Emit(OpCodes.Ldloc, localBuilders(local)) - } - } - - ////////////////////// branches /////////////////////// - - /** Returns a Triple (Boolean, Boolean, Option[Label]) - * - whether the jump leaves some exception block (try / catch / finally) - * - whether it leaves a finally handler (finally block, but not it's try / catch) - * - a label where to jump for leaving the finally handler - * . None to leave directly using `endfinally` - * . Some(label) to emit `leave label` (for try / catch inside a finally handler) - */ - def leavesHandler(from: BasicBlock, to: BasicBlock): (Boolean, Boolean, Option[Label]) = - if (currentHandlers.isEmpty) (false, false, None) - else { - val h = currentHandlers.head - val leaveHead = { h.covers(from) != h.covers(to) || - h.blocks.contains(from) != h.blocks.contains(to) } - if (leaveHead) { - // we leave the innermost exception block. - // find out if we also leave som e `finally` handler - currentHandlers.find(e => { - e.cls == NoSymbol && e.blocks.contains(from) != e.blocks.contains(to) - }) match { - case Some(finallyHandler) => - if (h == finallyHandler) { - // the finally handler is the innermost, so we can emit `endfinally` directly - (true, true, None) - } else { - // we need to `Leave` to the `endfinally` of the next outer finally handler - val l = endFinallyLabels.getOrElseUpdate(finallyHandler, mcode.DefineLabel()) - (true, true, Some(l)) - } - case None => - (true, false, None) - } - } else (false, false, None) - } - - def emitCondBr(block: BasicBlock, cond: TestOp, success: BasicBlock, failure: BasicBlock, - next: BasicBlock, emitBrFun: (TestOp, Label) => Unit) { - val (sLeaveHandler, sLeaveFinally, slfTarget) = leavesHandler(block, success) - val (fLeaveHandler, fLeaveFinally, flfTarget) = leavesHandler(block, failure) - - if (sLeaveHandler || fLeaveHandler) { - val sLabelOpt = if (sLeaveHandler) { - val leaveSLabel = mcode.DefineLabel() - emitBrFun(cond, leaveSLabel) - Some(leaveSLabel) - } else { - emitBrFun(cond, labels(success)) - None - } - - if (fLeaveHandler) { - if (fLeaveFinally) { - if (flfTarget.isDefined) mcode.Emit(OpCodes.Leave, flfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(failure)) - } else - mcode.Emit(OpCodes.Br, labels(failure)) - - sLabelOpt.map(l => { - mcode.MarkLabel(l) - if (sLeaveFinally) { - if (slfTarget.isDefined) mcode.Emit(OpCodes.Leave, slfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(success)) - }) - } else { - if (next == success) { - emitBrFun(cond.negate, labels(failure)) - } else { - emitBrFun(cond, labels(success)) - if (next != failure) { - mcode.Emit(OpCodes.Br, labels(failure)) - } - } - } - } - - def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) { - condition match { - case EQ => mcode.Emit(OpCodes.Beq, dest) - case NE => mcode.Emit(OpCodes.Bne_Un, dest) - case LT => mcode.Emit(if (isFloat) OpCodes.Blt_Un else OpCodes.Blt, dest) - case GE => mcode.Emit(if (isFloat) OpCodes.Bge_Un else OpCodes.Bge, dest) - case LE => mcode.Emit(if (isFloat) OpCodes.Ble_Un else OpCodes.Ble, dest) - case GT => mcode.Emit(if (isFloat) OpCodes.Bgt_Un else OpCodes.Bgt, dest) - } - } - - def emitBrBool(cond: TestOp, dest: Label) { - (cond: @unchecked) match { - // EQ -> Brfalse, NE -> Brtrue; this is because we come from - // a CZJUMP. If the value on the stack is 0 (e.g. a boolean - // method returned false), and we are in the case EQ, then - // we need to emit Brfalse (EQ Zero means false). vice versa - case EQ => mcode.Emit(OpCodes.Brfalse, dest) - case NE => mcode.Emit(OpCodes.Brtrue, dest) - } - } - - ////////////////////// local vars /////////////////////// - - /** - * Compute the indexes of each local variable of the given - * method. - */ - def computeLocalVarsIndex(m: IMethod) { - var idx = if (m.symbol.isStaticMember) 0 else 1 - - val params = m.params - for (l <- params) { - debuglog("Index value for parameter " + l + ": " + idx) - l.index = idx - idx += 1 // sizeOf(l.kind) - } - - val locvars = m.locals filterNot (params contains) - idx = 0 - - for (l <- locvars) { - debuglog("Index value for local variable " + l + ": " + idx) - l.index = idx - idx += 1 // sizeOf(l.kind) - } - - } - - ////////////////////// Utilities //////////////////////// - - /** Return the a name of this symbol that can be used on the .NET - * platform. It removes spaces from names. - * - * Special handling: scala.All and scala.AllRef are 'erased' to - * scala.All$ and scala.AllRef$. This is needed because they are - * not real classes, and they mean 'abrupt termination upon evaluation - * of that expression' or 'null' respectively. This handling is - * done already in GenICode, but here we need to remove references - * from method signatures to these types, because such classes can - * not exist in the classpath: the type checker will be very confused. - */ - def msilName(sym: Symbol): String = { - val suffix = sym.moduleSuffix - // Flags.JAVA: "symbol was not defined by a scala-class" (java, or .net-class) - - if (sym == definitions.NothingClass) - return "scala.runtime.Nothing$" - else if (sym == definitions.NullClass) - return "scala.runtime.Null$" - - (if (sym.isClass || (sym.isModule && !sym.isMethod)) { - if (sym.isNestedClass) sym.simpleName - else sym.fullName - } else - sym.simpleName.toString.trim()) + suffix - } - - - ////////////////////// flags /////////////////////// - - def msilTypeFlags(sym: Symbol): Int = { - var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass - - if(sym.isNestedClass) { - mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NestedPrivate else TypeAttributes.NestedPublic) - } else { - mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NotPublic else TypeAttributes.Public) - } - mf = mf | (if (sym hasFlag Flags.ABSTRACT) TypeAttributes.Abstract else 0) - mf = mf | (if (sym.isTrait && !sym.isImplClass) TypeAttributes.Interface else TypeAttributes.Class) - mf = mf | (if (sym isFinal) TypeAttributes.Sealed else 0) - mf - // static: not possible (or?) - } - - def msilMethodFlags(sym: Symbol): Short = { - var mf: Int = MethodAttributes.HideBySig | - (if (sym hasFlag Flags.PRIVATE) MethodAttributes.Private - else MethodAttributes.Public) - - if (!sym.isClassConstructor) { - if (sym.isStaticMember) - mf = mf | FieldAttributes.Static // coincidentally, same value as for MethodAttributes.Static ... - else { - mf = mf | MethodAttributes.Virtual - if (sym.isFinal && !getType(sym.owner).IsInterface) - mf = mf | MethodAttributes.Final - if (sym.isDeferred || getType(sym.owner).IsInterface) - mf = mf | MethodAttributes.Abstract - } - } - - if (sym.isStaticMember) { - mf = mf | MethodAttributes.Static - } - - // constructors of module classes should be private - if (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) { - mf |= MethodAttributes.Private - mf &= ~(MethodAttributes.Public) - } - - mf.toShort - } - - def msilFieldFlags(sym: Symbol): Short = { - var mf: Int = - if (sym hasFlag Flags.PRIVATE) FieldAttributes.Private - else if (sym hasFlag Flags.PROTECTED) FieldAttributes.FamORAssem - else FieldAttributes.Public - - if (sym hasFlag Flags.FINAL) - mf = mf | FieldAttributes.InitOnly - - if (sym.isStaticMember) - mf = mf | FieldAttributes.Static - - // TRANSIENT: "not serialized", VOLATILE: doesn't exist on .net - // TODO: add this annotation also if the class has the custom attribute - // System.NotSerializedAttribute - sym.annotations.foreach( a => a match { - case AnnotationInfo(TransientAtt, _, _) => - mf = mf | FieldAttributes.NotSerialized - case _ => () - }) - - mf.toShort - } - - ////////////////////// builders, types /////////////////////// - - var entryPoint: Symbol = _ - - val notInitializedModules = mutable.HashSet[Symbol]() - - // TODO: create fields also in def createType, and not in genClass, - // add a getField method (it only works as it is because fields never - // accessed from outside a class) - - val localBuilders = mutable.HashMap[Local, LocalBuilder]() - - private[GenMSIL] def findEntryPoint(cls: IClass) { - - def isEntryPoint(sym: Symbol):Boolean = { - if (isStaticModule(sym.owner) && msilName(sym) == "main") - if (sym.tpe.paramTypes.length == 1) { - toTypeKind(sym.tpe.paramTypes(0)) match { - case ARRAY(elem) => - if (elem.toType.typeSymbol == definitions.StringClass) { - return true - } - case _ => () - } - } - false - } - - if((entryPoint == null) && settings.Xshowcls.isSetByUser) { // TODO introduce dedicated setting instead - val entryclass = settings.Xshowcls.value.toString - val cfn = cls.symbol.fullName - if(cfn == entryclass) { - for (m <- cls.methods; if isEntryPoint(m.symbol)) { entryPoint = m.symbol } - if(entryPoint == null) { warning("Couldn't find main method in class " + cfn) } - } - } - - if (firstSourceName == "") - if (cls.symbol.sourceFile != null) // is null for nested classes - firstSourceName = cls.symbol.sourceFile.name - } - - // ##################################################################### - // get and create types - - private def msilType(t: TypeKind): MsilType = (t: @unchecked) match { - case UNIT => MVOID - case BOOL => MBOOL - case BYTE => MBYTE - case SHORT => MSHORT - case CHAR => MCHAR - case INT => MINT - case LONG => MLONG - case FLOAT => MFLOAT - case DOUBLE => MDOUBLE - case REFERENCE(cls) => getType(cls) - case ARRAY(elem) => - msilType(elem) match { - // For type builders, cannot call "clrTypes.mkArrayType" because this looks up - // the type "tp" in the assembly (not in the HashMap "types" of the backend). - // This can fail for nested types because the builders are not complete yet. - case tb: TypeBuilder => tb.MakeArrayType() - case tp: MsilType => clrTypes.mkArrayType(tp) - } - } - - private def msilType(tpe: Type): MsilType = msilType(toTypeKind(tpe)) - - private def msilParamTypes(sym: Symbol): Array[MsilType] = { - sym.tpe.paramTypes.map(msilType).toArray - } - - def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym))) - - /** - * Get an MSIL type from a symbol. First look in the clrTypes.types map, then - * lookup the name using clrTypes.getType - */ - def getTypeOpt(sym: Symbol): Option[MsilType] = { - val tmp = types.get(sym) - tmp match { - case typ @ Some(_) => typ - case None => - def typeString(sym: Symbol): String = { - val s = if (sym.isNestedClass) typeString(sym.owner) +"+"+ sym.simpleName - else sym.fullName - if (sym.isModuleClass && !sym.isTrait) s + "$" else s - } - val name = typeString(sym) - val typ = clrTypes.getType(name) - if (typ == null) - None - else { - types(sym) = typ - Some(typ) - } - } - } - - def mapType(sym: Symbol, mType: MsilType) { - assert(mType != null, showsym(sym)) - types(sym) = mType - } - - def createTypeBuilder(iclass: IClass) { - /** - * First look in the clrTypes.types map, if that fails check if it's a class being compiled, otherwise - * lookup by name (clrTypes.getType calls the static method msil.Type.GetType(fullname)). - */ - def msilTypeFromSym(sym: Symbol): MsilType = { - types.get(sym).getOrElse { - classes.get(sym) match { - case Some(iclass) => - msilTypeBuilderFromSym(sym) - case None => - getType(sym) - } - } - } - - def msilTypeBuilderFromSym(sym: Symbol): TypeBuilder = { - if(!(types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])){ - val iclass = classes(sym) - assert(iclass != null) - createTypeBuilder(iclass) - } - types(sym).asInstanceOf[TypeBuilder] - } - - val sym = iclass.symbol - if (types.contains(sym) && types(sym).isInstanceOf[TypeBuilder]) - return - - def isInterface(s: Symbol) = s.isTrait && !s.isImplClass - val parents: List[Type] = - if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe) - else sym.info.parents.distinct - - val superType : MsilType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol) - debuglog("super type: " + parents(0).typeSymbol + ", msil type: " + superType) - - val interfaces: Array[MsilType] = - parents.tail.map(p => msilTypeFromSym(p.typeSymbol)).toArray - if (parents.length > 1) { - if (settings.debug.value) { - log("interfaces:") - for (i <- 0.until(interfaces.length)) { - log(" type: " + parents(i + 1).typeSymbol + ", msil type: " + interfaces(i)) - } - } - } - - val tBuilder = if (sym.isNestedClass) { - val ownerT = msilTypeBuilderFromSym(sym.owner).asInstanceOf[TypeBuilder] - ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces) - } else { - mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces) - } - mapType(sym, tBuilder) - } // createTypeBuilder - - def createClassMembers(iclass: IClass) { - try { - createClassMembers0(iclass) - } - catch { - case e: Throwable => - java.lang.System.err.println(showsym(iclass.symbol)) - java.lang.System.err.println("with methods = " + iclass.methods) - throw e - } - } - - def createClassMembers0(iclass: IClass) { - - val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder] - - for (ifield <- iclass.fields) { - val sym = ifield.symbol - debuglog("Adding field: " + sym.fullName) - - val attributes = msilFieldFlags(sym) - val fieldTypeWithCustomMods = - new PECustomMod(msilType(sym.tpe), - customModifiers(sym.annotations)) - val fBuilder = mtype.DefineField(msilName(sym), - fieldTypeWithCustomMods, - attributes) - fields(sym) = fBuilder - addAttributes(fBuilder, sym.annotations) - } // all iclass.fields iterated over - - if (isStaticModule(iclass.symbol)) { - val sc = iclass.lookupStaticCtor - if (sc.isDefined) { - val m = sc.get - val oldLastBlock = m.lastBlock - val lastBlock = m.newBlock() - oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) - // call object's private ctor from static ctor - lastBlock.emit(CIL_NEWOBJ(iclass.symbol.primaryConstructor)) - lastBlock.emit(DROP(toTypeKind(iclass.symbol.tpe))) - lastBlock emit RETURN(UNIT) - lastBlock.close - } - } - - if (iclass.symbol != definitions.ArrayClass) { - for (m: IMethod <- iclass.methods) { - val sym = m.symbol - debuglog("Creating MethodBuilder for " + sym.flagString + " " + - sym.owner.fullName + "::" + sym.name) - - val ownerType = getType(sym.enclClass).asInstanceOf[TypeBuilder] - assert(mtype == ownerType, "mtype = " + mtype + "; ownerType = " + ownerType) - val paramTypes = msilParamTypes(sym) - val attr = msilMethodFlags(sym) - - if (m.symbol.isClassConstructor) { - val constr = - ownerType.DefineConstructor(attr, CallingConventions.Standard, paramTypes) - for (i <- 0.until(paramTypes.length)) { - constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) - } - mapConstructor(sym, constr) - addAttributes(constr, sym.annotations) - } else { - val resType = msilType(m.returnType) - val method = - ownerType.DefineMethod(msilName(sym), attr, resType, paramTypes) - for (i <- 0.until(paramTypes.length)) { - method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) - } - if (!methods.contains(sym)) - mapMethod(sym, method) - addAttributes(method, sym.annotations) - debuglog("\t created MethodBuilder " + method) - } - } - } // method builders created for non-array iclass - - if (isStaticModule(iclass.symbol)) { - addModuleInstanceField(iclass.symbol) - notInitializedModules += iclass.symbol - if (iclass.lookupStaticCtor.isEmpty) { - addStaticInit(iclass.symbol) - } - } - - } // createClassMembers0 - - private def isTopLevelModule(sym: Symbol): Boolean = - enteringRefchecks { - sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass - } - - // if the module is lifted it does not need to be initialized in - // its static constructor, and the MODULE$ field is not required. - // the outer class will care about it. - private def isStaticModule(sym: Symbol): Boolean = { - // .net inner classes: removed '!sym.hasFlag(Flags.LIFTED)', added - // 'sym.isStatic'. -> no longer compatible without skipping flatten! - sym.isModuleClass && sym.isStatic && !sym.isImplClass - } - - private def isCloneable(sym: Symbol): Boolean = { - !sym.annotations.forall( a => a match { - case AnnotationInfo(CloneableAttr, _, _) => false - case _ => true - }) - } - - private def addModuleInstanceField(sym: Symbol) { - debuglog("Adding Module-Instance Field for " + showsym(sym)) - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - val fb = tBuilder.DefineField(MODULE_INSTANCE_NAME, - tBuilder, - (FieldAttributes.Public | - //FieldAttributes.InitOnly | - FieldAttributes.Static).toShort) - fields(sym) = fb - } - - - // the symbol may be a object-symbol (module-symbol), or a module-class-symbol - private def getModuleInstanceField(sym: Symbol): FieldInfo = { - assert(sym.isModule || sym.isModuleClass, "Expected module: " + showsym(sym)) - - // when called by LOAD_MODULE, the corresponding type maybe doesn't - // exist yet -> make a getType - val moduleClassSym = if (sym.isModule) sym.moduleClass else sym - - // TODO: get module field for modules not defined in the - // source currently compiling (e.g. Console) - - fields get moduleClassSym match { - case Some(sym) => sym - case None => - //val mclass = types(moduleClassSym) - val nameInMetadata = nestingAwareFullClassname(moduleClassSym) - val mClass = clrTypes.getType(nameInMetadata) - val mfield = mClass.GetField("MODULE$") - assert(mfield ne null, "module not found " + showsym(moduleClassSym)) - fields(moduleClassSym) = mfield - mfield - } - - //fields(moduleClassSym) - } - - def nestingAwareFullClassname(csym: Symbol) : String = { - val suffix = csym.moduleSuffix - val res = if (csym.isNestedClass) - nestingAwareFullClassname(csym.owner) + "+" + csym.encodedName - else - csym.fullName - res + suffix - } - - /** Adds a static initializer which creates an instance of the module - * class (calls the primary constructor). A special primary constructor - * will be generated (notInitializedModules) which stores the new instance - * in the MODULE$ field right after the super call. - */ - private def addStaticInit(sym: Symbol) { - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - - val staticInit = tBuilder.DefineConstructor( - (MethodAttributes.Static | MethodAttributes.Public).toShort, - CallingConventions.Standard, - MsilType.EmptyTypes) - - val sicode = staticInit.GetILGenerator() - - val instanceConstructor = constructors(sym.primaryConstructor) - - // there are no constructor parameters. assuming the constructor takes no parameter - // is fine: we call (in the static constructor) the constructor of the module class, - // which takes no arguments - an object definition cannot take constructor arguments. - sicode.Emit(OpCodes.Newobj, instanceConstructor) - // the stsfld is done in the instance constructor, just after the super call. - sicode.Emit(OpCodes.Pop) - - sicode.Emit(OpCodes.Ret) - } - - private def generateMirrorClass(sym: Symbol) { - assert(sym.isModuleClass, "Can't generate Mirror-Class for the Non-Module class " + sym) - debuglog("Dumping mirror class for object: " + sym) - val moduleName = msilName(sym) - val mirrorName = moduleName.substring(0, moduleName.length() - 1) - val mirrorTypeBuilder = mmodule.DefineType(mirrorName, - TypeAttributes.Class | - TypeAttributes.Public | - TypeAttributes.Sealed, - MOBJECT, - MsilType.EmptyTypes) - - val iclass = classes(sym) - - for (m <- sym.tpe.nonPrivateMembers - if m.owner != definitions.ObjectClass && !m.isProtected && - m.isMethod && !m.isClassConstructor && !m.isStaticMember && !m.isCase && - !m.isDeferred) - { - debuglog(" Mirroring method: " + m) - val paramTypes = msilParamTypes(m) - val paramNames: Array[String] = new Array[String](paramTypes.length) - for (i <- 0 until paramTypes.length) - paramNames(i) = "x_" + i - - // CHECK: verify if getMethodName is better than msilName - val mirrorMethod = mirrorTypeBuilder.DefineMethod(msilName(m), - (MethodAttributes.Public | - MethodAttributes.Static).toShort, - msilType(m.tpe.resultType), - paramTypes) - - var i = 0 - while (i < paramTypes.length) { - mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i)) - i += 1 - } - - val mirrorCode = mirrorMethod.GetILGenerator() - mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym)) - val mInfo = getMethod(m) - for (paramidx <- 0.until(paramTypes.length)) { - val mInfoParams = mInfo.GetParameters - val loadAddr = mInfoParams(paramidx).ParameterType.IsByRef - loadArg(mirrorCode, loadAddr)(paramidx) - } - - mirrorCode.Emit(OpCodes.Callvirt, getMethod(m)) - mirrorCode.Emit(OpCodes.Ret) - } - - addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder) - - mirrorTypeBuilder.CreateType() - mirrorTypeBuilder.setSourceFilepath(iclass.cunit.source.file.path) - } - - - // ##################################################################### - // delegate callers - - var delegateCallers: TypeBuilder = _ - var nbDelegateCallers: Int = 0 - - private def initDelegateCallers() = { - delegateCallers = mmodule.DefineType("$DelegateCallers", TypeAttributes.Public | - TypeAttributes.Sealed) - } - - private def createDelegateCaller(functionType: Type, delegateType: Type) = { - if (delegateCallers == null) - initDelegateCallers() - // create a field an store the function-object - val mFunctionType: MsilType = msilType(functionType) - val anonfunField: FieldBuilder = delegateCallers.DefineField( - "$anonfunField$$" + nbDelegateCallers, mFunctionType, - (FieldAttributes.InitOnly | FieldAttributes.Public | FieldAttributes.Static).toShort) - mcode.Emit(OpCodes.Stsfld, anonfunField) - - - // create the static caller method and the delegate object - val (params, returnType) = delegateType.member(nme.apply).tpe match { - case MethodType(delParams, delReturn) => (delParams, delReturn) - case _ => abort("not a delegate type: " + delegateType) - } - val caller: MethodBuilder = delegateCallers.DefineMethod( - "$delegateCaller$$" + nbDelegateCallers, - (MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static).toShort, - msilType(returnType), (params map (_.tpe)).map(msilType).toArray) - for (i <- 0 until params.length) - caller.DefineParameter(i, ParameterAttributes.None, "arg" + i) // FIXME: use name of parameter symbol - val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) - mcode.Emit(OpCodes.Ldnull) - mcode.Emit(OpCodes.Ldftn, caller) - mcode.Emit(OpCodes.Newobj, delegCtor) - - - // create the static caller method body - val functionApply: MethodInfo = getMethod(functionType.member(nme.apply)) - val dcode: ILGenerator = caller.GetILGenerator() - dcode.Emit(OpCodes.Ldsfld, anonfunField) - for (i <- 0 until params.length) { - loadArg(dcode, false /* TODO confirm whether passing actual as-is to formal is correct wrt the ByRef attribute of the param */)(i) - emitBox(dcode, toTypeKind(params(i).tpe)) - } - dcode.Emit(OpCodes.Callvirt, functionApply) - emitUnbox(dcode, toTypeKind(returnType)) - dcode.Emit(OpCodes.Ret) - - nbDelegateCallers = nbDelegateCallers + 1 - - } //def createDelegateCaller - - def emitBox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match { - // doesn't make sense, unit as parameter.. - case UNIT => code.Emit(OpCodes.Ldsfld, boxedUnit) - case BOOL | BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE => - code.Emit(OpCodes.Box, msilType(boxType)) - case REFERENCE(cls) if clrTypes.isValueType(cls) => - code.Emit(OpCodes.Box, (msilType(boxType))) - case REFERENCE(_) | ARRAY(_) => - warning("Tried to BOX a non-valuetype.") - () - } - - def emitUnbox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match { - case UNIT => code.Emit(OpCodes.Pop) - /* (1) it's essential to keep the code emitted here (as of now plain calls to System.Convert.ToBlaBla methods) - behaviorally.equiv.wrt. BoxesRunTime.unboxToBlaBla methods - (case null: that's easy, case boxed: track changes to unboxBlaBla) - (2) See also: asInstanceOf to cast from Any to number, - tracked in http://lampsvn.epfl.ch/trac/scala/ticket/4437 */ - case BOOL => code.Emit(OpCodes.Call, toBool) - case BYTE => code.Emit(OpCodes.Call, toSByte) - case SHORT => code.Emit(OpCodes.Call, toShort) - case CHAR => code.Emit(OpCodes.Call, toChar) - case INT => code.Emit(OpCodes.Call, toInt) - case LONG => code.Emit(OpCodes.Call, toLong) - case FLOAT => code.Emit(OpCodes.Call, toFloat) - case DOUBLE => code.Emit(OpCodes.Call, toDouble) - case REFERENCE(cls) if clrTypes.isValueType(cls) => - code.Emit(OpCodes.Unbox, msilType(boxType)) - code.Emit(OpCodes.Ldobj, msilType(boxType)) - case REFERENCE(_) | ARRAY(_) => - warning("Tried to UNBOX a non-valuetype.") - () - } - - // ##################################################################### - // get and create methods / constructors - - def getConstructor(sym: Symbol): ConstructorInfo = constructors.get(sym) match { - case Some(constr) => constr - case None => - val mClass = getType(sym.owner) - val constr = mClass.GetConstructor(msilParamTypes(sym)) - if (constr eq null) { - java.lang.System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name) - java.lang.System.out.println("scope = " + sym.owner.tpe.decls) - abort(sym.fullName) - } - else { - mapConstructor(sym, constr) - constr - } - } - - def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = { - constructors(sym) = cInfo - } - - private def getMethod(sym: Symbol): MethodInfo = { - - methods.get(sym) match { - case Some(method) => method - case None => - val mClass = getType(sym.owner) - try { - val method = mClass.GetMethod(msilName(sym), msilParamTypes(sym), - msilType(sym.tpe.resultType)) - if (method eq null) { - java.lang.System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym)) - java.lang.System.out.println("scope = " + sym.owner.tpe.decls) - abort(sym.fullName) - } - else { - mapMethod(sym, method) - method - } - } - catch { - case e: Exception => - Console.println("While looking up " + mClass + "::" + sym.nameString) - Console.println("\t" + showsym(sym)) - throw e - } - } - } - - /* - * add a mapping between sym and mInfo - */ - private def mapMethod(sym: Symbol, mInfo: MethodInfo) { - assert (mInfo != null, mInfo) - methods(sym) = mInfo - } - - private def showsym(sym: Symbol): String = (sym.toString + - "\n symbol = " + sym.flagString + " " + sym + - "\n owner = " + sym.owner.flagString + " " + sym.owner - ) - - } // class BytecodeGenerator - -} // class GenMSIL diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index 650775b259..5dd20a6919 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -146,18 +146,18 @@ abstract class ClosureElimination extends SubComponent { case _ => } - case UNBOX(_) => + case UNBOX(boxType) => info.stack match { case Deref(LocalVar(loc1)) :: _ if info.bindings isDefinedAt LocalVar(loc1) => val value = info.getBinding(loc1) value match { - case Boxed(LocalVar(loc2)) => + case Boxed(LocalVar(loc2)) if loc2.kind == boxType => bb.replaceInstruction(i, DROP(icodes.ObjectReference) :: valueToInstruction(info.getBinding(loc2)) :: Nil) debuglog("replaced " + i + " with " + info.getBinding(loc2)) case _ => () } - case Boxed(LocalVar(loc1)) :: _ => + case Boxed(LocalVar(loc1)) :: _ if loc1.kind == boxType => val loc2 = info.getAlias(loc1) bb.replaceInstruction(i, DROP(icodes.ObjectReference) :: valueToInstruction(Deref(LocalVar(loc2))) :: Nil) debuglog("replaced " + i + " with " + LocalVar(loc2)) diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 04f95455a5..4dac5b7d90 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -233,8 +233,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * exists, but should not be documented (either it's not included in the source or it's not visible) */ class NoDocTemplateImpl(sym: Symbol, inTpl: TemplateImpl) extends EntityImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with NoDocTemplate { - assert(modelFinished) - assert(!(noDocTemplatesCache isDefinedAt sym)) + assert(modelFinished, this) + assert(!(noDocTemplatesCache isDefinedAt sym), (sym, noDocTemplatesCache(sym))) noDocTemplatesCache += (sym -> this) def isDocTemplate = false } @@ -269,7 +269,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * All ancestors of the template and all non-package members. */ abstract class DocTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberTemplateImpl(sym, inTpl) with DocTemplateEntity { - assert(!modelFinished) + assert(!modelFinished, (sym, inTpl)) assert(!(docTemplatesCache isDefinedAt sym), sym) docTemplatesCache += (sym -> this) @@ -620,7 +620,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { */ def createTemplate(aSym: Symbol, inTpl: DocTemplateImpl): Option[MemberImpl] = { // don't call this after the model finished! - assert(!modelFinished) + assert(!modelFinished, (aSym, inTpl)) def createRootPackageComment: Option[Comment] = if(settings.docRootContent.isDefault) None @@ -636,7 +636,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def createDocTemplate(bSym: Symbol, inTpl: DocTemplateImpl): DocTemplateImpl = { - assert(!modelFinished) // only created BEFORE the model is finished + assert(!modelFinished, (bSym, inTpl)) // only created BEFORE the model is finished if (bSym.isAliasType && bSym != AnyRefClass) new DocTemplateImpl(bSym, inTpl) with AliasImpl with AliasType { override def isAliasType = true } else if (bSym.isAbstractType) @@ -842,24 +842,28 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { lazy val annotationClass = makeTemplate(annot.symbol) val arguments = { // lazy - def noParams = annot.args map { _ => None } + def annotArgs = annot.args match { + case Nil => annot.assocs collect { case (_, LiteralAnnotArg(const)) => Literal(const) } + case xs => xs + } + def noParams = annotArgs map (_ => None) + val params: List[Option[ValueParam]] = annotationClass match { case aClass: DocTemplateEntity with Class => (aClass.primaryConstructor map { _.valueParams.head }) match { case Some(vps) => vps map { Some(_) } - case None => noParams + case _ => noParams } case _ => noParams } - assert(params.length == annot.args.length) - (params zip annot.args) flatMap { case (param, arg) => - makeTree(arg) match { - case Some(tree) => - Some(new ValueArgument { - def parameter = param - def value = tree - }) - case None => None + assert(params.length == annotArgs.length, (params, annotArgs)) + + params zip annotArgs flatMap { case (param, arg) => + makeTree(arg) map { tree => + new ValueArgument { + def parameter = param + def value = tree + } } } } @@ -891,7 +895,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // units.filter should return only one element (currentRun.units filter (_.source.file == aSym.sourceFile)).toList match { case List(unit) => - (unit.body find (_.symbol == aSym)) match { + // SI-4922 `sym == aSym` is insufficent if `aSym` is a clone of symbol + // of the parameter in the tree, as can happen with type parametric methods. + def isCorrespondingParam(sym: Symbol) = ( + sym != null && + sym != NoSymbol && + sym.owner == aSym.owner && + sym.name == aSym.name && + sym.isParamWithDefault + ) + (unit.body find (t => isCorrespondingParam(t.symbol))) match { case Some(ValDef(_,_,_,rhs)) => makeTree(rhs) case _ => None } diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index e4bff1e192..9716c75215 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -810,8 +810,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") respond(response) { scopeMembers(pos) } } - private val Dollar = newTermName("$") - private class Members[M <: Member] extends LinkedHashMap[Name, Set[M]] { override def default(key: Name) = Set() @@ -827,7 +825,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") def add(sym: Symbol, pre: Type, implicitlyAdded: Boolean)(toMember: (Symbol, Type) => M) { if ((sym.isGetter || sym.isSetter) && sym.accessed != NoSymbol) { add(sym.accessed, pre, implicitlyAdded)(toMember) - } else if (!sym.name.decodedName.containsName(Dollar) && !sym.isSynthetic && sym.hasRawInfo) { + } else if (!sym.name.decodedName.containsName("$") && !sym.isSynthetic && sym.hasRawInfo) { val symtpe = pre.memberType(sym) onTypeError ErrorType matching(sym, symtpe, this(sym.name)) match { case Some(m) => diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index ecaa793da7..5a1a4cbdeb 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -143,7 +143,7 @@ self: scala.tools.nsc.Global => */ private def setChildrenPos(pos: Position, trees: List[Tree]): Unit = try { for (tree <- trees) { - if (!tree.isEmpty && tree.pos == NoPosition) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { val children = tree.children if (children.isEmpty) { tree setPos pos.focus @@ -164,7 +164,7 @@ self: scala.tools.nsc.Global => */ override def atPos[T <: Tree](pos: Position)(tree: T): T = { if (pos.isOpaqueRange) { - if (!tree.isEmpty && tree.pos == NoPosition) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { tree.setPos(pos) val children = tree.children if (children.nonEmpty) { @@ -202,7 +202,7 @@ self: scala.tools.nsc.Global => def validate(tree: Tree, encltree: Tree): Unit = { - if (!tree.isEmpty) { + if (!tree.isEmpty && tree.canHaveAttrs) { if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value)) println("[%10s] %s".format("validate", treeStatus(tree, encltree))) diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index fcb485defd..9fbd337b9d 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -7,7 +7,7 @@ package interpreter import scala.tools.nsc.io.AbstractFile import util.ScalaClassLoader -import java.net.URL +import java.net.{ URL, URLConnection, URLStreamHandler } import scala.collection.{ mutable, immutable } /** @@ -25,7 +25,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) protected def findAbstractFile(name: String): AbstractFile = { var file: AbstractFile = root - val pathParts = classNameToPath(name) split '/' + val pathParts = name split '/' for (dirPart <- pathParts.init) { file = file.lookupName(dirPart, true) @@ -55,11 +55,26 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) return file } + // parent delegation in JCL uses getResource; so either add parent.getResAsStream + // or implement findResource, which we do here as a study in scarlet (my complexion + // after looking at CLs and URLs) + override def findResource(name: String): URL = findAbstractFile(name) match { + case null => null + case file => new URL(null, "repldir:" + file.path, new URLStreamHandler { + override def openConnection(url: URL): URLConnection = new URLConnection(url) { + override def connect() { } + override def getInputStream = file.input + } + }) + } + + // this inverts delegation order: super.getResAsStr calls parent.getRes if we fail override def getResourceAsStream(name: String) = findAbstractFile(name) match { case null => super.getResourceAsStream(name) case file => file.input } - override def classBytes(name: String): Array[Byte] = findAbstractFile(name) match { + // ScalaClassLoader.classBytes uses getResAsStream, so we'll try again before delegating + override def classBytes(name: String): Array[Byte] = findAbstractFile(classNameToPath(name)) match { case null => super.classBytes(name) case file => file.toByteArray } diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 612a90f3ea..b2af53574f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -279,14 +279,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - protected def newJavap() = { - val intp = ILoop.this.intp - import intp._ + protected def newJavap() = + JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp), Some(intp)) - new JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp)) { - override def tryClass(path: String) = super.tryClass(translatePath(path) getOrElse path) - } - } private lazy val javap = substituteAndLog[Javap]("javap", NoJavap)(newJavap()) // Still todo: modules. @@ -308,8 +303,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) private def javapCommand(line: String): Result = { if (javap == null) ":javap unavailable, no tools.jar at %s. Set JDK_HOME.".format(jdkHome) - else if (javaVersion startsWith "1.7") - ":javap not yet working with java 1.7" else if (line == "") ":javap [-lcsvp] [path1 path2 ...]" else diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 3f49e782b0..91e909b1f1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -312,6 +312,14 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends case _ => Some(flatPath(sym)) } } + def translateEnclosingClass(n: String) = { + def enclosingClass(s: Symbol): Symbol = + if (s == NoSymbol || s.isClass) s else enclosingClass(s.owner) + enclosingClass(symbolOfTerm(n)) match { + case NoSymbol => None + case c => Some(flatPath(c)) + } + } private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(replOutput.dir, parent) { /** Overridden here to try translating a simple name to the generated diff --git a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala index d114ca2359..9541d08db1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala @@ -25,7 +25,7 @@ class ISettings(intp: IMain) { var maxAutoprintCompletion = 250 /** String unwrapping can be disabled if it is causing issues. - * Settings this to false means you will see Strings like "$iw.$iw.". + * Setting this to false means you will see Strings like "$iw.$iw.". */ var unwrapStrings = true diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 381dfeb261..cb091ac9a7 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -116,7 +116,7 @@ trait MemberHandlers { if (replProps.vids) s"""" + " @ " + "%%8x".format(System.identityHashCode($path)) + " """.trim else "" - """ + "%s%s: %s = " + %s""".format(prettyName, vidString, string2code(req typeOf name), resultString) + """ + "%s%s: %s = " + %s""".format(string2code(prettyName), vidString, string2code(req typeOf name), resultString) } } } @@ -143,13 +143,12 @@ trait MemberHandlers { override def resultExtractionCode(req: Request) = { val lhsType = string2code(req lookupTypeOf name) val res = string2code(req fullPath name) - - """ + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) + "\n" + """ + "%s: %s = " + %s + "\n" """.format(string2code(lhs.toString), lhsType, res) + "\n" } } class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) { - override def definesTerm = Some(name) + override def definesTerm = Some(name.toTermName) override def definesValue = true override def resultExtractionCode(req: Request) = codegenln("defined module ", name) diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala index 41ddf23de4..57f3675ada 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala @@ -6,6 +6,8 @@ package scala.tools.nsc package interpreter +import scala.util.Properties.lineSeparator + /** This is for name logic which is independent of the compiler (notice there's no Global.) * That includes at least generating, metaquoting, mangling, and unmangling. */ @@ -18,8 +20,14 @@ trait Naming { // <ESC> for ansi codes. val binaryChars = cleaned count (ch => ch < 32 && !ch.isWhitespace && ch != ESC) // Lots of binary chars - translate all supposed whitespace into spaces - if (binaryChars > 5) - cleaned map (ch => if (ch.isWhitespace) ' ' else if (ch < 32) '?' else ch) + // except supposed line endings, otherwise scrubbed lines run together + if (binaryChars > 5) // more than one can count while holding a hamburger + cleaned map { + case c if lineSeparator contains c => c + case c if c.isWhitespace => ' ' + case c if c < 32 => '?' + case c => c + } // Not lots - preserve whitespace and ESC else cleaned map (ch => if (ch.isWhitespace || ch == ESC) ch else if (ch < 32) '?' else ch) diff --git a/src/compiler/scala/tools/nsc/io/MsilFile.scala b/src/compiler/scala/tools/nsc/io/MsilFile.scala deleted file mode 100644 index bda13a5ed0..0000000000 --- a/src/compiler/scala/tools/nsc/io/MsilFile.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package io - -import ch.epfl.lamp.compiler.msil.{ Type => MsilType } - -/** This class wraps an MsilType. It exists only so - * ClassPath can treat all of JVM/MSIL/bin/src files - * uniformly, as AbstractFiles. - */ -class MsilFile(val msilType: MsilType) extends VirtualFile(msilType.FullName, msilType.Namespace) { } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 73cbeaa6c4..bb82cfb827 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -125,7 +125,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def makeSyntheticParam(count: Int, tpt: Tree): ValDef = makeParam(nme.syntheticParamName(count), tpt) def makeParam(name: String, tpt: Tree): ValDef = - makeParam(newTypeName(name), tpt) + makeParam(name: TermName, tpt) def makeParam(name: TermName, tpt: Tree): ValDef = ValDef(Modifiers(Flags.JAVA | Flags.PARAM), name, tpt, EmptyTree) @@ -448,7 +448,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { AppliedTypeTree(scalaDot(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME), List(t)) } } - varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM), t, ident()) + varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM), t, ident().toTermName) } def optThrows() { @@ -506,7 +506,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { if (parentToken == AT && in.token == DEFAULT) { val annot = atPos(pos) { - New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), ListOfNil) + New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil) } mods1 = mods1 withAnnotations List(annot) skipTo(SEMI) @@ -542,7 +542,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { * these potential definitions are real or not. */ def fieldDecls(pos: Position, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = { - val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name)) + val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name.toTermName)) val maybe = new ListBuffer[Tree] // potential variable definitions. while (in.token == COMMA) { in.nextToken @@ -550,10 +550,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val name = ident() if (in.token == ASSIGN || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition buf ++= maybe - buf += varDecl(in.currentPos, mods, tpt.duplicate, name) + buf += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName) maybe.clear() } else if (in.token == COMMA) { // ... if there's a comma after the ident, it could be a real vardef or not. - maybe += varDecl(in.currentPos, mods, tpt.duplicate, name) + maybe += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName) } else { // ... if there's something else we were still in the initializer of the // previous var def; skip to next comma or semicolon. skipTo(COMMA, SEMI) @@ -830,7 +830,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { // The STABLE flag is to signal to namer that this was read from a // java enum, and so should be given a Constant type (thereby making // it usable in annotations.) - ValDef(Modifiers(Flags.STABLE | Flags.JAVA | Flags.STATIC), name, enumType, blankExpr) + ValDef(Modifiers(Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr) } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index cf6579a696..8f964cf9e1 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -69,10 +69,6 @@ trait ScalaSettings extends AbsScalaSettings * -X "Advanced" settings */ val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") - val assemname = StringSetting ("-Xassem-name", "file", "(Requires -target:msil) Name of the output assembly.", "").dependsOn(target, "msil") - val assemrefs = StringSetting ("-Xassem-path", "path", "(Requires -target:msil) List of assemblies referenced by the program.", ".").dependsOn(target, "msil") - val assemextdirs = StringSetting ("-Xassem-extdirs", "dirs", "(Requires -target:msil) List of directories containing assemblies. default:lib", Defaults.scalaLibDir.path).dependsOn(target, "msil") - val sourcedir = StringSetting ("-Xsourcedir", "directory", "(Requires -target:msil) Mirror source folder structure in output directory.", ".").dependsOn(target, "msil") val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") val developer = BooleanSetting ("-Xdev", "Indicates user is a developer - issue warnings about anything which seems amiss") val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 98ef74aee3..9338d9e5b5 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -41,8 +41,7 @@ trait StandardScalaSettings { val optimise: BooleanSetting // depends on post hook which mutates other settings val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.") val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", - List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil"), - "jvm-1.6") + List("jvm-1.5", "jvm-1.6", "jvm-1.7"), "jvm-1.6") val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions.") val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.") diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 19502f0d7e..bbff03f67f 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -12,7 +12,7 @@ import scala.tools.nsc.util.{ ClassPath } import classfile.ClassfileParser import scala.reflect.internal.MissingRequirementError import scala.reflect.internal.util.Statistics -import scala.tools.nsc.io.{ AbstractFile, MsilFile } +import scala.tools.nsc.io.{ AbstractFile } /** This class ... * @@ -152,7 +152,7 @@ abstract class SymbolLoaders { def sourcefile: Option[AbstractFile] = None /** - * Description of the resource (ClassPath, AbstractFile, MsilFile) + * Description of the resource (ClassPath, AbstractFile) * being processed by this loader */ protected def description: String @@ -265,16 +265,6 @@ abstract class SymbolLoaders { override def sourcefile: Option[AbstractFile] = classfileParser.srcfile } - class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader with FlagAssigningCompleter { - private def typ = msilFile.msilType - private object typeParser extends clr.TypeParser { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - } - - protected def description = "MsilFile "+ typ.FullName + ", assembly "+ typ.Assembly.FullName - protected def doComplete(root: Symbol) { typeParser.parse(typ, root) } - } - class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter { protected def description = "source file "+ srcfile.toString override def fromSource = true @@ -287,11 +277,6 @@ abstract class SymbolLoaders { protected def doComplete(root: Symbol) { root.sourceModule.initialize } } - object clrTypes extends clr.CLRTypes { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - if (global.forMSIL) init() - } - /** used from classfile parser to avoid cyclies */ var parentsLevel = 0 var pendingLoadActions: List[() => Unit] = Nil diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 6d213af2b6..cb58111b51 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -252,8 +252,8 @@ abstract class ClassfileParser { } else { log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe) f = tpe match { - case MethodType(_, _) => owner.newMethod(name, owner.pos) - case _ => owner.newVariable(name, owner.pos) + case MethodType(_, _) => owner.newMethod(name.toTermName, owner.pos) + case _ => owner.newVariable(name.toTermName, owner.pos) } f setInfo tpe log("created fake member " + f.fullName) @@ -282,7 +282,7 @@ abstract class ClassfileParser { if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start) val name = getName(in.getChar(start + 1).toInt) // create a dummy symbol for method types - val dummySym = ownerTpe.typeSymbol.newMethod(name, ownerTpe.typeSymbol.pos) + val dummySym = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) var tpe = getType(dummySym, in.getChar(start + 3).toInt) // fix the return type, which is blindly set to the class currently parsed @@ -457,7 +457,7 @@ abstract class ClassfileParser { ss = name.subName(start, end) sym = owner.info.decls lookup ss if (sym == NoSymbol) { - sym = owner.newPackage(ss) setInfo completer + sym = owner.newPackage(ss.toTermName) setInfo completer sym.moduleClass setInfo completer owner.info.decls enter sym } @@ -604,7 +604,7 @@ abstract class ClassfileParser { } else { val name = pool.getName(in.nextChar) val info = pool.getType(in.nextChar) - val sym = getOwner(jflags).newValue(name, NoPosition, sflags) + val sym = getOwner(jflags).newValue(name.toTermName, NoPosition, sflags) val isEnum = (jflags & JAVA_ACC_ENUM) != 0 sym setInfo { @@ -639,7 +639,7 @@ abstract class ClassfileParser { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) - val sym = getOwner(jflags).newMethod(name, NoPosition, sflags) + val sym = getOwner(jflags).newMethod(name.toTermName, NoPosition, sflags) var info = pool.getType(sym, (in.nextChar)) if (name == nme.CONSTRUCTOR) info match { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index b5459ec773..79b08bcabf 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -80,7 +80,7 @@ abstract class ICodeReader extends ClassfileParser { val jflags = in.nextChar val name = pool getName in.nextChar val owner = getOwner(jflags) - val dummySym = owner.newMethod(name, owner.pos, toScalaMethodFlags(jflags)) + val dummySym = owner.newMethod(name.toTermName, owner.pos, toScalaMethodFlags(jflags)) try { val ch = in.nextChar @@ -94,7 +94,7 @@ abstract class ICodeReader extends ClassfileParser { if (sym == NoSymbol) sym = owner.info.findMember(newTermName(name + nme.LOCAL_SUFFIX_STRING), 0, 0, false).suchThat(_.tpe =:= tpe) if (sym == NoSymbol) { - sym = if (field) owner.newValue(name, owner.pos, toScalaFieldFlags(jflags)) else dummySym + sym = if (field) owner.newValue(name.toTermName, owner.pos, toScalaFieldFlags(jflags)) else dummySym sym setInfoAndEnter tpe log(s"ICodeReader could not locate ${name.decode} in $owner. Created ${sym.defString}.") } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 42ea7e61f0..caa45ea6db 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -206,7 +206,7 @@ abstract class Pickler extends SubComponent { case RefinedType(parents, decls) => val rclazz = tp.typeSymbol for (m <- decls.iterator) - if (m.owner != rclazz) assert(false, "bad refinement member "+m+" of "+tp+", owner = "+m.owner) + if (m.owner != rclazz) abort("bad refinement member "+m+" of "+tp+", owner = "+m.owner) putSymbol(rclazz); putTypes(parents); putSymbols(decls.toList) case ClassInfoType(parents, decls, clazz) => putSymbol(clazz); putTypes(parents); putSymbols(decls.toList) diff --git a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala deleted file mode 100644 index 624db027f1..0000000000 --- a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala +++ /dev/null @@ -1,132 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2004-2013 LAMP/EPFL - */ - - -package scala.tools.nsc -package symtab -package clr - -import ch.epfl.lamp.compiler.msil._ -import scala.collection.{ mutable, immutable } - -/** - * Collects all types from all reference assemblies. - */ -abstract class CLRTypes { - - val global: Global - import global.Symbol - - //########################################################################## - - var BYTE: Type = _ - var UBYTE: Type = _ - var SHORT: Type = _ - var USHORT: Type = _ - var CHAR: Type = _ - var INT: Type = _ - var UINT: Type = _ - var LONG: Type = _ - var ULONG: Type = _ - var FLOAT: Type = _ - var DOUBLE: Type = _ - var BOOLEAN: Type = _ - var VOID: Type = _ - var ENUM: Type = _ - var DELEGATE: Type = _ - - var OBJECT: Type = _ - var STRING: Type = _ - var STRING_ARRAY: Type = _ - - var VALUE_TYPE: Type = _ - - var SCALA_SYMTAB_ATTR: Type = _ - var SYMTAB_CONSTR: ConstructorInfo = _ - var SYMTAB_DEFAULT_CONSTR: ConstructorInfo = _ - - var DELEGATE_COMBINE: MethodInfo = _ - var DELEGATE_REMOVE: MethodInfo = _ - - val types: mutable.Map[Symbol,Type] = new mutable.HashMap - val constructors: mutable.Map[Symbol,ConstructorInfo] = new mutable.HashMap - val methods: mutable.Map[Symbol,MethodInfo] = new mutable.HashMap - val fields: mutable.Map[Symbol, FieldInfo] = new mutable.HashMap - val sym2type: mutable.Map[Type,Symbol] = new mutable.HashMap - val addressOfViews = new mutable.HashSet[Symbol] - val mdgptrcls4clssym: mutable.Map[ /*cls*/ Symbol, /*cls*/ Symbol] = new mutable.HashMap - - def isAddressOf(msym : Symbol) = addressOfViews.contains(msym) - - def isNonEnumValuetype(cls: Symbol) = { - val msilTOpt = types.get(cls) - val res = msilTOpt.isDefined && { - val msilT = msilTOpt.get - msilT.IsValueType && !msilT.IsEnum - } - res - } - - def isValueType(cls: Symbol): Boolean = { - val opt = types.get(cls) - opt.isDefined && opt.get.IsValueType - } - - def init() = try { // initialize - // the MsilClasspath (nsc/util/Classpath.scala) initializes the msil-library by calling - // Assembly.LoadFrom("mscorlib.dll"), so this type should be found - Type.initMSCORLIB(getTypeSafe("System.String").Assembly) - - BYTE = getTypeSafe("System.SByte") - UBYTE = getTypeSafe("System.Byte") - CHAR = getTypeSafe("System.Char") - SHORT = getTypeSafe("System.Int16") - USHORT = getTypeSafe("System.UInt16") - INT = getTypeSafe("System.Int32") - UINT = getTypeSafe("System.UInt32") - LONG = getTypeSafe("System.Int64") - ULONG = getTypeSafe("System.UInt64") - FLOAT = getTypeSafe("System.Single") - DOUBLE = getTypeSafe("System.Double") - BOOLEAN = getTypeSafe("System.Boolean") - VOID = getTypeSafe("System.Void") - ENUM = getTypeSafe("System.Enum") - DELEGATE = getTypeSafe("System.MulticastDelegate") - - OBJECT = getTypeSafe("System.Object") - STRING = getTypeSafe("System.String") - STRING_ARRAY = getTypeSafe("System.String[]") - VALUE_TYPE = getTypeSafe("System.ValueType") - - SCALA_SYMTAB_ATTR = getTypeSafe("scala.runtime.SymtabAttribute") - val bytearray: Array[Type] = Array(Type.GetType("System.Byte[]")) - SYMTAB_CONSTR = SCALA_SYMTAB_ATTR.GetConstructor(bytearray) - SYMTAB_DEFAULT_CONSTR = SCALA_SYMTAB_ATTR.GetConstructor(Type.EmptyTypes) - - val delegate: Type = getTypeSafe("System.Delegate") - val dargs: Array[Type] = Array(delegate, delegate) - DELEGATE_COMBINE = delegate.GetMethod("Combine", dargs) - DELEGATE_REMOVE = delegate.GetMethod("Remove", dargs) - } - catch { - case e: RuntimeException => - Console.println(e.getMessage) - throw e - } - - //########################################################################## - // type mapping and lookup - - def getType(name: String): Type = Type.GetType(name) - - def getTypeSafe(name: String): Type = { - val t = Type.GetType(name) - assert(t != null, name) - t - } - - def mkArrayType(elemType: Type): Type = getType(elemType.FullName + "[]") - - def isDelegateType(t: Type): Boolean = { t.BaseType() == DELEGATE } -} // CLRTypes diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala deleted file mode 100644 index f0e49ce500..0000000000 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ /dev/null @@ -1,849 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2004-2013 LAMP/EPFL - */ - -package scala.tools.nsc -package symtab -package clr - -import java.io.IOException -import io.MsilFile -import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, _} -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.pickling.UnPickler -import ch.epfl.lamp.compiler.msil.Type.TMVarUsage - -/** - * @author Nikolay Mihaylov - */ -abstract class TypeParser { - - val global: Global - - import global._ - import loaders.clrTypes - - //########################################################################## - - private var clazz: Symbol = _ - private var instanceDefs: Scope = _ // was members - private var staticModule: Symbol = _ // was staticsClass - private var staticDefs: Scope = _ // was statics - - protected def statics: Symbol = staticModule.moduleClass - - protected var busy: Boolean = false // lock to detect recursive reads - - private object unpickler extends UnPickler { - val global: TypeParser.this.global.type = TypeParser.this.global - } - - def parse(typ: MSILType, root: Symbol) { - - def handleError(e: Throwable) = { - if (settings.debug.value) e.printStackTrace() //debug - throw new IOException("type '" + typ.FullName + "' is broken\n(" + e.getMessage() + ")") - } - assert(!busy) - busy = true - - if (root.isModule) { - this.clazz = root.companionClass - this.staticModule = root - } else { - this.clazz = root - this.staticModule = root.companionModule - } - try { - parseClass(typ) - } catch { - case e: FatalError => handleError(e) - case e: RuntimeException => handleError(e) - } - busy = false - } - - class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { - override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } - } - - /* the names `classTParams` and `newTParams` stem from the forJVM version (ClassfileParser.sigToType()) - * but there are differences that should be kept in mind. - * forMSIL, a nested class knows nothing about any type-params in the nesting class, - * therefore newTParams is redundant (other than for recording lexical order), - * it always contains the same elements as classTParams.value */ - val classTParams = scala.collection.mutable.Map[Int,Symbol]() // TODO should this be a stack? (i.e., is it possible for >1 invocation to getCLRType on the same TypeParser instance be active ) - val newTParams = new scala.collection.mutable.ListBuffer[Symbol]() - val methodTParams = scala.collection.mutable.Map[Int,Symbol]() - - private def sig2typeBounds(tvarCILDef: GenericParamAndConstraints): Type = { - val ts = new scala.collection.mutable.ListBuffer[Type] - for (cnstrnt <- tvarCILDef.Constraints) { - ts += getCLRType(cnstrnt) // TODO we're definitely not at or after erasure, no need to call objToAny, right? - } - TypeBounds.upper(intersectionType(ts.toList, clazz)) - // TODO variance??? - } - - private def createViewFromTo(viewSuffix : String, fromTpe : Type, toTpe : Type, - addToboxMethodMap : Boolean, isAddressOf : Boolean) : Symbol = { - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? - val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(List(fromTpe)), toTpe) - val vmsym = createMethod(nme.view_ + viewSuffix, flags, viewMethodType, null, true); - // !!! this used to mutate a mutable map in definitions, but that map became - // immutable and this kept "working" with a no-op. So now it's commented out - // since I retired the deprecated code which allowed for that bug. - // - // if (addToboxMethodMap) definitions.boxMethod(clazz) = vmsym - - if (isAddressOf) clrTypes.addressOfViews += vmsym - vmsym - } - - private def createDefaultConstructor(typ: MSILType) { - val attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName // TODO instance - val declType= typ - val method = new ConstructorInfo(declType, attrs, Array[MSILType]()) - val flags = Flags.JAVA - val owner = clazz - val methodSym = owner.newMethod(nme.CONSTRUCTOR, NoPosition, flags) - val rettype = clazz.tpe - val mtype = methodType(Array[MSILType](), rettype); - val mInfo = mtype(methodSym) - methodSym.setInfo(mInfo) - instanceDefs.enter(methodSym); - clrTypes.constructors(methodSym) = method - } - - private def parseClass(typ: MSILType) { - - { - val t4c = clrTypes.types.get(clazz) - assert(t4c == None || t4c == Some(typ)) - } - clrTypes.types(clazz) = typ - - { - val c4t = clrTypes.sym2type.get(typ) - assert(c4t == None || c4t == Some(clazz)) - } - clrTypes.sym2type(typ) = clazz - - if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) { - val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false); - assert (attrs.length == 1, attrs.length); - val a = attrs(0).asInstanceOf[MSILAttribute]; - assert (a.getConstructor() == clrTypes.SYMTAB_CONSTR); - val symtab = a.getConstructorArguments()(0).asInstanceOf[Array[Byte]] - unpickler.unpickle(symtab, 0, clazz, staticModule, typ.FullName); - val mClass = clrTypes.getType(typ.FullName + "$"); - if (mClass != null) { - clrTypes.types(statics) = mClass; - val moduleInstance = mClass.GetField("MODULE$"); - assert (moduleInstance != null, mClass); - clrTypes.fields(statics) = moduleInstance; - } - return - } - val flags = translateAttributes(typ) - - var clazzBoxed : Symbol = NoSymbol - var clazzMgdPtr : Symbol = NoSymbol - - val canBeTakenAddressOf = (typ.IsValueType || typ.IsEnum) && (typ.FullName != "System.Enum") - - if(canBeTakenAddressOf) { - clazzBoxed = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("Boxed")) - clazzMgdPtr = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("MgdPtr")) - clrTypes.mdgptrcls4clssym(clazz) = clazzMgdPtr - /* adding typMgdPtr to clrTypes.sym2type should happen early (before metadata for supertypes is parsed, - before metadata for members are parsed) so that clazzMgdPtr can be found by getClRType. */ - val typMgdPtr = MSILType.mkByRef(typ) - clrTypes.types(clazzMgdPtr) = typMgdPtr - clrTypes.sym2type(typMgdPtr) = clazzMgdPtr - /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance, - because there's no metadata-level representation for a "boxed valuetype" */ - val instanceDefsMgdPtr = newScope - val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr) - clazzMgdPtr.setFlag(flags) - clazzMgdPtr.setInfo(classInfoMgdPtr) - } - -/* START CLR generics (snippet 1) */ - // first pass - for (tvarCILDef <- typ.getSortedTVars() ) { - val tpname = newTypeName(tvarCILDef.Name.replaceAll("!", "")) // TODO are really all type-params named in all assemblies out there? (NO) - val tpsym = clazz.newTypeParameter(tpname) - classTParams.put(tvarCILDef.Number, tpsym) - newTParams += tpsym - // TODO wouldn't the following also be needed later, i.e. during getCLRType - tpsym.setInfo(definitions.AnyClass.tpe) - } - // second pass - for (tvarCILDef <- typ.getSortedTVars() ) { - val tpsym = classTParams(tvarCILDef.Number) - tpsym.setInfo(sig2typeBounds(tvarCILDef)) // we never skip bounds unlike in forJVM - } -/* END CLR generics (snippet 1) */ - val ownTypeParams = newTParams.toList -/* START CLR generics (snippet 2) */ - if (!ownTypeParams.isEmpty) { - clazz.setInfo(new TypeParamsType(ownTypeParams)) - if(typ.IsValueType && !typ.IsEnum) { - clazzBoxed.setInfo(new TypeParamsType(ownTypeParams)) - } - } -/* END CLR generics (snippet 2) */ - instanceDefs = newScope - staticDefs = newScope - - val classInfoAsInMetadata = { - val ifaces: Array[MSILType] = typ.getInterfaces() - val superType = if (typ.BaseType() != null) getCLRType(typ.BaseType()) - else if (typ.IsInterface()) definitions.ObjectClass.tpe - else definitions.AnyClass.tpe; // this branch activates for System.Object only. - // parents (i.e., base type and interfaces) - val parents = new scala.collection.mutable.ListBuffer[Type]() - parents += superType - for (iface <- ifaces) { - parents += getCLRType(iface) // here the variance doesn't matter - } - // methods, properties, events, fields are entered in a moment - if (canBeTakenAddressOf) { - val instanceDefsBoxed = newScope - ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed) - } else - ClassInfoType(parents.toList, instanceDefs, clazz) - } - - val staticInfo = ClassInfoType(List(), staticDefs, statics) - - clazz.setFlag(flags) - - if (canBeTakenAddressOf) { - clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else genPolyType(ownTypeParams, classInfoAsInMetadata) ) - clazzBoxed.setFlag(flags) - val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz) - clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType - else genPolyType(ownTypeParams, rawValueInfoType) ) - } else { - clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else genPolyType(ownTypeParams, classInfoAsInMetadata) ) - } - - // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params - statics.setFlag(Flags.JAVA) - statics.setInfo(staticInfo) - staticModule.setFlag(Flags.JAVA) - staticModule.setInfo(statics.tpe) - - - if (canBeTakenAddressOf) { - // implicit conversions are owned by staticModule.moduleClass - createViewFromTo("2Boxed", clazz.tpe, clazzBoxed.tpe, addToboxMethodMap = true, isAddressOf = false) - // createViewFromTo("2Object", clazz.tpe, definitions.ObjectClass.tpe, addToboxMethodMap = true, isAddressOf = false) - createViewFromTo("2MgdPtr", clazz.tpe, clazzMgdPtr.tpe, addToboxMethodMap = false, isAddressOf = true) - // a return can't have type managed-pointer, thus a dereference-conversion is not needed - // similarly, a method can't declare as return type "boxed valuetype" - if (!typ.IsEnum) { - // a synthetic default constructor for raw-type allows `new X' syntax - createDefaultConstructor(typ) - } - } - - // import nested types - for (ntype <- typ.getNestedTypes() if !(ntype.IsNestedPrivate || ntype.IsNestedAssembly || ntype.IsNestedFamANDAssem) - || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ ) - { - val loader = new loaders.MsilFileLoader(new MsilFile(ntype)) - val nclazz = statics.newClass(ntype.Name) - val nmodule = statics.newModule(ntype.Name) - nclazz.setInfo(loader) - nmodule.setInfo(loader) - staticDefs.enter(nclazz) - staticDefs.enter(nmodule) - - assert(nclazz.companionModule == nmodule, nmodule) - assert(nmodule.companionClass == nclazz, nclazz) - } - - val fields = typ.getFields() - for (field <- fields - if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly) - if (getCLRType(field.FieldType) != null) - ) { - assert (!field.FieldType.IsPointer && !field.FieldType.IsByRef, "CLR requirement") - val flags = translateAttributes(field); - val name = newTermName(field.Name); - val fieldType = - if (field.IsLiteral && !field.FieldType.IsEnum && isDefinedAtgetConstant(getCLRType(field.FieldType))) - ConstantType(getConstant(getCLRType(field.FieldType), field.getValue)) - else - getCLRType(field.FieldType) - val owner = if (field.IsStatic()) statics else clazz; - val sym = owner.newValue(name, NoPosition, flags).setInfo(fieldType); - // TODO: set private within!!! -> look at typechecker/Namers.scala - (if (field.IsStatic()) staticDefs else instanceDefs).enter(sym); - clrTypes.fields(sym) = field; - } - - for (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() && - !constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType()) - createMethod(constr); - - // initially also contains getters and setters of properties. - val methodsSet = new mutable.HashSet[MethodInfo](); - methodsSet ++= typ.getMethods(); - - for (prop <- typ.getProperties) { - val propType: Type = getCLSType(prop.PropertyType); - if (propType != null) { - val getter: MethodInfo = prop.GetGetMethod(true); - val setter: MethodInfo = prop.GetSetMethod(true); - var gparamsLength: Int = -1; - if (!(getter == null || getter.IsPrivate || getter.IsAssembly - || getter.IsFamilyAndAssembly || getter.HasPtrParamOrRetType)) - { - assert(prop.PropertyType == getter.ReturnType); - val gparams: Array[ParameterInfo] = getter.GetParameters(); - gparamsLength = gparams.length; - val name: TermName = if (gparamsLength == 0) prop.Name else nme.apply; - val flags = translateAttributes(getter); - val owner: Symbol = if (getter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(name, NoPosition, flags) - val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic - else methodType(getter, getter.ReturnType)(methodSym) - methodSym.setInfo(mtype); - methodSym.setFlag(Flags.ACCESSOR); - (if (getter.IsStatic) staticDefs else instanceDefs).enter(methodSym) - clrTypes.methods(methodSym) = getter; - methodsSet -= getter; - } - if (!(setter == null || setter.IsPrivate || setter.IsAssembly - || setter.IsFamilyAndAssembly || setter.HasPtrParamOrRetType)) - { - val sparams: Array[ParameterInfo] = setter.GetParameters() - if(getter != null) - assert(getter.IsStatic == setter.IsStatic); - assert(setter.ReturnType == clrTypes.VOID); - if(getter != null) - assert(sparams.length == gparamsLength + 1, "" + getter + "; " + setter); - - val name: TermName = if (gparamsLength == 0) nme.getterToSetter(prop.Name) - else nme.update; - val flags = translateAttributes(setter); - val mtype = methodType(setter, definitions.UnitClass.tpe); - val owner: Symbol = if (setter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(name, NoPosition, flags) - methodSym.setInfo(mtype(methodSym)) - methodSym.setFlag(Flags.ACCESSOR); - (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym); - clrTypes.methods(methodSym) = setter; - methodsSet -= setter; - } - } - } - -/* for (event <- typ.GetEvents) { - // adding += and -= methods to add delegates to an event. - // raising the event ist not possible from outside the class (this is so - // generally in .net world) - val adder: MethodInfo = event.GetAddMethod(); - val remover: MethodInfo = event.GetRemoveMethod(); - if (!(adder == null || adder.IsPrivate || adder.IsAssembly - || adder.IsFamilyAndAssembly)) - { - assert(adder.ReturnType == clrTypes.VOID); - assert(adder.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType)); - val name = encode("+="); - val flags = translateAttributes(adder); - val mtype: Type = methodType(adder, adder.ReturnType); - createMethod(name, flags, mtype, adder, adder.IsStatic) - methodsSet -= adder; - } - if (!(remover == null || remover.IsPrivate || remover.IsAssembly - || remover.IsFamilyAndAssembly)) - { - assert(remover.ReturnType == clrTypes.VOID); - assert(remover.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType)); - val name = encode("-="); - val flags = translateAttributes(remover); - val mtype: Type = methodType(remover, remover.ReturnType); - createMethod(name, flags, mtype, remover, remover.IsStatic) - methodsSet -= remover; - } - } */ - -/* Adds view amounting to syntax sugar for a CLR implicit overload. - The long-form syntax can also be supported if "methodsSet -= method" (last statement) is removed. - - /* remember, there's typ.getMethods and type.GetMethods */ - for (method <- typ.getMethods) - if(!method.HasPtrParamOrRetType && - method.IsPublic && method.IsStatic && method.IsSpecialName && - method.Name == "op_Implicit") { - // create a view: typ => method's return type - val viewRetType: Type = getCLRType(method.ReturnType) - val viewParamTypes: List[Type] = method.GetParameters().map(_.ParameterType).map(getCLSType).toList; - /* The spec says "The operator method shall be defined as a static method on either the operand or return type." - * We don't consider the declaring type for the purposes of definitions.functionType, - * instead we regard op_Implicit's argument type and return type as defining the view's signature. - */ - if (viewRetType != null && !viewParamTypes.contains(null)) { - /* The check above applies e.g. to System.Decimal that has a conversion from UInt16, a non-CLS type, whose CLS-mapping returns null */ - val funType: Type = definitions.functionType(viewParamTypes, viewRetType); - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? - val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(viewParamTypes), funType) - val vmsym = createMethod(nme.view_, flags, viewMethodType, method, true); - methodsSet -= method; - } - } -*/ - - for (method <- methodsSet.iterator) - if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly() - && !method.HasPtrParamOrRetType) - createMethod(method); - - // Create methods and views for delegate support - if (clrTypes.isDelegateType(typ)) { - createDelegateView(typ) - createDelegateChainers(typ) - } - - // for enumerations introduce comparison and bitwise logical operations; - // the backend will recognize them and replace them with comparison or - // bitwise logical operations on the primitive underlying type - - if (typ.IsEnum) { - val ENUM_CMP_NAMES = List(nme.EQ, nme.NE, nme.LT, nme.LE, nme.GT, nme.GE); - val ENUM_BIT_LOG_NAMES = List(nme.OR, nme.AND, nme.XOR); - - val flags = Flags.JAVA | Flags.FINAL - for (cmpName <- ENUM_CMP_NAMES) { - val enumCmp = clazz.newMethod(cmpName) - val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe) - enumCmp.setFlag(flags).setInfo(enumCmpType) - instanceDefs.enter(enumCmp) - } - - for (bitLogName <- ENUM_BIT_LOG_NAMES) { - val enumBitLog = clazz.newMethod(bitLogName) - val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */) - enumBitLog.setFlag(flags).setInfo(enumBitLogType) - instanceDefs.enter(enumBitLog) - } - } - - } // parseClass - - private def populateMethodTParams(method: MethodBase, methodSym: MethodSymbol) : List[Symbol] = { - if(!method.IsGeneric) Nil - else { - methodTParams.clear - val newMethodTParams = new scala.collection.mutable.ListBuffer[Symbol]() - - // first pass - for (mvarCILDef <- method.getSortedMVars() ) { - val mtpname = newTypeName(mvarCILDef.Name.replaceAll("!", "")) // TODO are really all method-level-type-params named in all assemblies out there? (NO) - val mtpsym = methodSym.newTypeParameter(mtpname) - methodTParams.put(mvarCILDef.Number, mtpsym) - newMethodTParams += mtpsym - // TODO wouldn't the following also be needed later, i.e. during getCLRType - mtpsym.setInfo(definitions.AnyClass.tpe) - } - // second pass - for (mvarCILDef <- method.getSortedMVars() ) { - val mtpsym = methodTParams(mvarCILDef.Number) - mtpsym.setInfo(sig2typeBounds(mvarCILDef)) // we never skip bounds unlike in forJVM - } - - newMethodTParams.toList - } - } - - private def createMethod(method: MethodBase) { - - val flags = translateAttributes(method); - val owner = if (method.IsStatic()) statics else clazz; - val methodSym = owner.newMethod(getName(method), NoPosition, flags) - /* START CLR generics (snippet 3) */ - val newMethodTParams = populateMethodTParams(method, methodSym) - /* END CLR generics (snippet 3) */ - - val rettype = if (method.IsConstructor()) clazz.tpe - else getCLSType(method.asInstanceOf[MethodInfo].ReturnType); - if (rettype == null) return; - val mtype = methodType(method, rettype); - if (mtype == null) return; -/* START CLR generics (snippet 4) */ - val mInfo = if (method.IsGeneric) genPolyType(newMethodTParams, mtype(methodSym)) - else mtype(methodSym) -/* END CLR generics (snippet 4) */ -/* START CLR non-generics (snippet 4) - val mInfo = mtype(methodSym) - END CLR non-generics (snippet 4) */ - methodSym.setInfo(mInfo) - (if (method.IsStatic()) staticDefs else instanceDefs).enter(methodSym); - if (method.IsConstructor()) - clrTypes.constructors(methodSym) = method.asInstanceOf[ConstructorInfo] - else clrTypes.methods(methodSym) = method.asInstanceOf[MethodInfo]; - } - - private def createMethod(name: TermName, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = { - val mtype = methodType(args, getCLSType(retType)) - assert(mtype != null) - createMethod(name, flags, mtype, method, statik) - } - - private def createMethod(name: TermName, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { - val methodSym: Symbol = (if (statik) statics else clazz).newMethod(name) - methodSym.setFlag(flags).setInfo(mtype(methodSym)) - (if (statik) staticDefs else instanceDefs).enter(methodSym) - if (method != null) - clrTypes.methods(methodSym) = method - methodSym - } - - private def createDelegateView(typ: MSILType) = { - val invoke: MethodInfo = typ.GetMember("Invoke")(0).asInstanceOf[MethodInfo]; - val invokeRetType: Type = getCLRType(invoke.ReturnType); - val invokeParamTypes: List[Type] =invoke.GetParameters().map(_.ParameterType).map(getCLSType).toList; - val funType: Type = definitions.functionType(invokeParamTypes, invokeRetType); - - val typClrType: Type = getCLRType(typ); - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? think not needed - - // create the forward view: delegate => function - val delegateParamTypes: List[Type] = List(typClrType); - // not ImplicitMethodType, this is for methods with implicit parameters (not implicit methods) - val forwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(delegateParamTypes), funType) - createMethod(nme.view_, flags, forwardViewMethodType, null, true); - - // create the backward view: function => delegate - val functionParamTypes: List[Type] = List(funType); - val backwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(functionParamTypes), typClrType) - createMethod(nme.view_, flags, backwardViewMethodType, null, true); - } - - private def createDelegateChainers(typ: MSILType) = { - val flags: Long = Flags.JAVA | Flags.FINAL - val args: Array[MSILType] = Array(typ) - - var s = createMethod(encode("+="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_COMBINE, false); - s = createMethod(encode("-="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_REMOVE, false); - - s = createMethod(nme.PLUS, flags, args, typ, clrTypes.DELEGATE_COMBINE, false); - s = createMethod(nme.MINUS, flags, args, typ, clrTypes.DELEGATE_REMOVE, false); - } - - private def getName(method: MethodBase): TermName = { - - def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match { - case 1 => name match { - // PartitionI.10.3.1 - case "op_Decrement" => Some(encode("--")) - case "op_Increment" => Some(encode("++")) - case "op_UnaryNegation" => Some(nme.UNARY_-) - case "op_UnaryPlus" => Some(nme.UNARY_+) - case "op_LogicalNot" => Some(nme.UNARY_!) - case "op_OnesComplement" => Some(nme.UNARY_~) - /* op_True and op_False have no operator symbol assigned, - Other methods that will have to be written in full are: - op_AddressOf & (unary) - op_PointerDereference * (unary) */ - case _ => None - } - case 2 => name match { - // PartitionI.10.3.2 - case "op_Addition" => Some(nme.ADD) - case "op_Subtraction" => Some(nme.SUB) - case "op_Multiply" => Some(nme.MUL) - case "op_Division" => Some(nme.DIV) - case "op_Modulus" => Some(nme.MOD) - case "op_ExclusiveOr" => Some(nme.XOR) - case "op_BitwiseAnd" => Some(nme.AND) - case "op_BitwiseOr" => Some(nme.OR) - case "op_LogicalAnd" => Some(nme.ZAND) - case "op_LogicalOr" => Some(nme.ZOR) - case "op_LeftShift" => Some(nme.LSL) - case "op_RightShift" => Some(nme.ASR) - case "op_Equality" => Some(nme.EQ) - case "op_GreaterThan" => Some(nme.GT) - case "op_LessThan" => Some(nme.LT) - case "op_Inequality" => Some(nme.NE) - case "op_GreaterThanOrEqual" => Some(nme.GE) - case "op_LessThanOrEqual" => Some(nme.LE) - - /* op_MemberSelection is reserved in Scala */ - - /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift , - * and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */ - - /* - The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full. - - op_RightShiftAssignment >>= - op_MultiplicationAssignment *= - op_PointerToMemberSelection ->* - op_SubtractionAssignment -= - op_ExclusiveOrAssignment ^= - op_LeftShiftAssignment <<= - op_ModulusAssignment %= - op_AdditionAssignment += - op_BitwiseAndAssignment &= - op_BitwiseOrAssignment |= - op_Comma , - op_DivisionAssignment /= - */ - case _ => None - } - case _ => None - } - - if (method.IsConstructor()) return nme.CONSTRUCTOR; - val name = method.Name; - if (method.IsStatic()) { - if(method.IsSpecialName) { - val paramsArity = method.GetParameters().size - // handle operator overload, otherwise handle as any static method - val operName = operatorOverload(name, paramsArity) - if (operName.isDefined) { return operName.get; } - } - return newTermName(name); - } - val params = method.GetParameters(); - name match { - case "GetHashCode" if (params.length == 0) => nme.hashCode_; - case "ToString" if (params.length == 0) => nme.toString_; - case "Finalize" if (params.length == 0) => nme.finalize_; - case "Equals" if (params.length == 1 && params(0).ParameterType == clrTypes.OBJECT) => - nme.equals_; - case "Invoke" if (clrTypes.isDelegateType(method.DeclaringType)) => nme.apply; - case _ => newTermName(name); - } - } - - //########################################################################## - - private def methodType(method: MethodBase, rettype: MSILType): Symbol => Type = { - val rtype = getCLSType(rettype); - if (rtype == null) null else methodType(method, rtype); - } - - /** Return a method type for the given method. */ - private def methodType(method: MethodBase, rettype: Type): Symbol => Type = - methodType(method.GetParameters().map(_.ParameterType), rettype); - - /** Return a method type for the provided argument types and return type. */ - private def methodType(argtypes: Array[MSILType], rettype: Type): Symbol => Type = { - def paramType(typ: MSILType): Type = - if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe // TODO a hack to compile scalalib, should be definitions.AnyRefClass.tpe - else getCLSType(typ); - val ptypes = argtypes.map(paramType).toList; - if (ptypes.contains(null)) null - else method => JavaMethodType(method.newSyntheticValueParams(ptypes), rettype); - } - - //########################################################################## - - private def getClassType(typ: MSILType): Type = { - assert(typ != null); - val res = rootMirror.getClassByName(typ.FullName.replace('+', '.') : TypeName).tpe; - //if (res.isError()) - // global.reporter.error("unknown class reference " + type.FullName); - res - } - - private def getCLSType(typ: MSILType): Type = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not - if (typ.IsTMVarUsage()) - /* START CLR generics (snippet 5) */ - getCLRType(typ) - /* END CLR generics (snippet 5) */ - /* START CLR non-generics (snippet 5) - null - END CLR non-generics (snippet 5) */ - else if ( /* TODO hack if UBYE, uncommented, "ambiguous reference to overloaded definition" ensues, for example for System.Math.Max(x, y) */ - typ == clrTypes.USHORT || typ == clrTypes.UINT || typ == clrTypes.ULONG - /* || typ == clrTypes.UBYTE */ - || typ.IsNotPublic() || typ.IsNestedPrivate() - || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem() - || typ.IsPointer() - || (typ.IsArray() && getCLRType(typ.GetElementType()) == null) /* TODO hack: getCLR instead of getCLS */ - || (typ.IsByRef() && !typ.GetElementType().CanBeTakenAddressOf())) - null - else - getCLRType(typ) - } - - private def getCLRTypeIfPrimitiveNullOtherwise(typ: MSILType): Type = - if (typ == clrTypes.OBJECT) - definitions.ObjectClass.tpe; - else if (typ == clrTypes.VALUE_TYPE) - definitions.AnyValClass.tpe - else if (typ == clrTypes.STRING) - definitions.StringClass.tpe; - else if (typ == clrTypes.VOID) - definitions.UnitClass.tpe - else if (typ == clrTypes.BOOLEAN) - definitions.BooleanClass.tpe - else if (typ == clrTypes.CHAR) - definitions.CharClass.tpe - else if ((typ == clrTypes.BYTE) || (typ == clrTypes.UBYTE)) // TODO U... is a hack to compile scalalib - definitions.ByteClass.tpe - else if ((typ == clrTypes.SHORT) || (typ == clrTypes.SHORT)) // TODO U... is a hack to compile scalalib - definitions.ShortClass.tpe - else if ((typ == clrTypes.INT) || (typ == clrTypes.UINT)) // TODO U... is a hack to compile scalalib - definitions.IntClass.tpe - else if ((typ == clrTypes.LONG) || (typ == clrTypes.LONG)) // TODO U... is a hack to compile scalalib - definitions.LongClass.tpe - else if (typ == clrTypes.FLOAT) - definitions.FloatClass.tpe - else if (typ == clrTypes.DOUBLE) - definitions.DoubleClass.tpe - else null - - - private def getCLRType(tMSIL: MSILType): Type = { - var res = getCLRTypeIfPrimitiveNullOtherwise(tMSIL) - if (res != null) res - else if (tMSIL.isInstanceOf[ConstructedType]) { - val ct = tMSIL.asInstanceOf[ConstructedType] - /* START CLR generics (snippet 6) */ - val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList - appliedType(getCLRType(ct.instantiatedType), cttpArgs) - /* END CLR generics (snippet 6) */ - /* START CLR non-generics (snippet 6) - getCLRType(ct.instantiatedType) - END CLR non-generics (snippet 6) */ - } else if (tMSIL.isInstanceOf[TMVarUsage]) { - /* START CLR generics (snippet 7) */ - val tVarUsage = tMSIL.asInstanceOf[TMVarUsage] - val tVarNumber = tVarUsage.Number - if (tVarUsage.isTVar) classTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst - else methodTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst - /* END CLR generics (snippet 7) */ - /* START CLR non-generics (snippet 7) - null // definitions.ObjectClass.tpe - END CLR non-generics (snippet 7) */ - } else if (tMSIL.IsArray()) { - var elemtp = getCLRType(tMSIL.GetElementType()) - // cut&pasted from ClassfileParser - // make unbounded Array[T] where T is a type variable into Array[T with Object] - // (this is necessary because such arrays have a representation which is incompatible - // with arrays of primitive types). - // TODO does that incompatibility also apply to .NET? - if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe)) - elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe)) - appliedType(definitions.ArrayClass.tpe, List(elemtp)) - } else { - res = clrTypes.sym2type.get(tMSIL) match { - case Some(sym) => sym.tpe - case None => if (tMSIL.IsByRef && tMSIL.GetElementType.IsValueType) { - val addressed = getCLRType(tMSIL.GetElementType) - val clasym = addressed.typeSymbolDirect // TODO should be .typeSymbol? - clasym.info.load(clasym) - val secondAttempt = clrTypes.sym2type.get(tMSIL) - secondAttempt match { case Some(sym) => sym.tpe - case None => null - } - } else getClassType(tMSIL) - } - if (res == null) - null // TODO new RuntimeException() - else res - } - } - - // the values are Java-Box-Classes (e.g. Integer, Boolean, Character) - // java.lang.Number to get the value (if a number, not for boolean, character) - // see ch.epfl.lamp.compiler.msil.util.PEStream.java - def getConstant(constType: Type, value: Object): Constant = { - val typeClass = constType.typeSymbol - if (typeClass == definitions.BooleanClass) - Constant(value.asInstanceOf[java.lang.Boolean].booleanValue) - else if (typeClass == definitions.ByteClass) - Constant(value.asInstanceOf[java.lang.Number].byteValue) - else if (typeClass == definitions.ShortClass) - Constant(value.asInstanceOf[java.lang.Number].shortValue) - else if (typeClass == definitions.CharClass) - Constant(value.asInstanceOf[java.lang.Character].charValue) - else if (typeClass == definitions.IntClass) - Constant(value.asInstanceOf[java.lang.Number].intValue) - else if (typeClass == definitions.LongClass) - Constant(value.asInstanceOf[java.lang.Number].longValue) - else if (typeClass == definitions.FloatClass) - Constant(value.asInstanceOf[java.lang.Number].floatValue) - else if (typeClass == definitions.DoubleClass) - Constant(value.asInstanceOf[java.lang.Number].doubleValue) - else if (typeClass == definitions.StringClass) - Constant(value.asInstanceOf[java.lang.String]) - else - abort("illegal value: " + value + ", class-symbol: " + typeClass) - } - - def isDefinedAtgetConstant(constType: Type): Boolean = { - val typeClass = constType.typeSymbol - if ( (typeClass == definitions.BooleanClass) - || (typeClass == definitions.ByteClass) - || (typeClass == definitions.ShortClass) - || (typeClass == definitions.CharClass) - || (typeClass == definitions.IntClass) - || (typeClass == definitions.LongClass) - || (typeClass == definitions.FloatClass) - || (typeClass == definitions.DoubleClass) - || (typeClass == definitions.StringClass) - ) - true - else - false - } - - private def translateAttributes(typ: MSILType): Long = { - var flags: Long = Flags.JAVA; - if (typ.IsNotPublic() || typ.IsNestedPrivate() - || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem()) - flags = flags | Flags.PRIVATE; - else if (typ.IsNestedFamily() || typ.IsNestedFamORAssem()) - flags = flags | Flags.PROTECTED; - if (typ.IsAbstract()) - flags = flags | Flags.ABSTRACT; - if (typ.IsSealed()) - flags = flags | Flags.FINAL; - if (typ.IsInterface()) - flags = flags | Flags.INTERFACE | Flags.TRAIT | Flags.ABSTRACT; - - flags - } - - private def translateAttributes(field: FieldInfo): Long = { - var flags: Long = Flags.JAVA; - if (field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly()) - flags = flags | Flags.PRIVATE; - else if (field.IsFamily() || field.IsFamilyOrAssembly()) - flags = flags | Flags.PROTECTED; - if (field.IsInitOnly() || field.IsLiteral()) - flags = flags | Flags.FINAL; - else - flags = flags | Flags.MUTABLE; - if (field.IsStatic) - flags = flags | Flags.STATIC - - flags - } - - private def translateAttributes(method: MethodBase): Long = { - var flags: Long = Flags.JAVA; - if (method.IsPrivate() || method.IsAssembly() || method.IsFamilyAndAssembly()) - flags = flags | Flags.PRIVATE; - else if (method.IsFamily() || method.IsFamilyOrAssembly()) - flags = flags | Flags.PROTECTED; - if (method.IsAbstract()) - flags = flags | Flags.DEFERRED; - if (method.IsStatic) - flags = flags | Flags.STATIC - - flags - } -} diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 847ca574a9..39460ef004 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -69,12 +69,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL { case "mono-cache" => MONO_CACHE case "poly-cache" => POLY_CACHE } - - def shouldRewriteTry(tree: Try) = { - val sym = tree.tpe.typeSymbol - forMSIL && (sym != UnitClass) && (sym != NothingClass) - } - private def typedWithPos(pos: Position)(tree: Tree) = localTyper.typedPos(pos)(tree) @@ -120,7 +114,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { } def addStaticMethodToClass(forBody: (Symbol, Symbol) => Tree): Symbol = { - val methSym = currentClass.newMethod(mkTerm(nme.reflMethodName), ad.pos, STATIC | SYNTHETIC) + val methSym = currentClass.newMethod(mkTerm(nme.reflMethodName.toString), ad.pos, STATIC | SYNTHETIC) val params = methSym.newSyntheticValueParams(List(ClassClass.tpe)) methSym setInfoAndEnter MethodType(params, MethodClass.tpe) @@ -543,10 +537,9 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * constructor. */ case Template(parents, self, body) => localTyper = typer.atOwner(tree, currentClass) - if (forMSIL) savingStatics( transformTemplate(tree) ) - else transformTemplate(tree) + transformTemplate(tree) - case Literal(c) if (c.tag == ClazzTag) && !forMSIL=> + case Literal(c) if c.tag == ClazzTag => val tpe = c.typeValue typedWithPos(tree.pos) { if (isPrimitiveValueClass(tpe.typeSymbol)) { @@ -559,24 +552,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL { else tree } - /* MSIL requires that the stack is empty at the end of a try-block. - * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they - * store their result in a local variable. The catch blocks are adjusted as well. - * The try tree is subsituted by a block whose result expression is read of that variable. */ - case theTry @ Try(block, catches, finalizer) if shouldRewriteTry(theTry) => - def transformTry = { - val tpe = theTry.tpe.widen - val tempVar = currentOwner.newVariable(mkTerm(nme.EXCEPTION_RESULT_PREFIX), theTry.pos).setInfo(tpe) - def assignBlock(rhs: Tree) = super.transform(BLOCK(Ident(tempVar) === transform(rhs))) - - val newBlock = assignBlock(block) - val newCatches = for (CaseDef(pattern, guard, body) <- catches) yield - (CASE(super.transform(pattern)) IF (super.transform(guard))) ==> assignBlock(body) - val newTry = Try(newBlock, newCatches, super.transform(finalizer)) - - typedWithPos(theTry.pos)(BLOCK(VAL(tempVar) === EmptyTree, newTry, Ident(tempVar))) - } - transformTry /* * This transformation should identify Scala symbol invocations in the tree and replace them * with references to a static member. Also, whenever a class has at least a single symbol invocation @@ -645,9 +620,8 @@ abstract class CleanUp extends Transform with ast.TreeDSL { // create a symbol for the static field val stfieldSym = ( currentClass.newVariable(mkTerm("symbol$"), pos, PRIVATE | STATIC | SYNTHETIC | FINAL) - setInfo SymbolClass.tpe + setInfoAndEnter SymbolClass.tpe ) - currentClass.info.decls enter stfieldSym // create field definition and initialization val stfieldDef = theTyper.typedPos(pos)(VAL(stfieldSym) === rhs) diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 323c7c7261..e99b42a402 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -60,7 +60,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { // The constructor parameter corresponding to an accessor def parameter(acc: Symbol): Symbol = - parameterNamed(nme.getterName(acc.originalName)) + parameterNamed(nme.getterName(acc.originalName.toTermName)) // The constructor parameter with given name. This means the parameter // has given name, or starts with given name, and continues with a `$` afterwards. @@ -68,7 +68,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def matchesName(param: Symbol) = param.name == name || param.name.startsWith(name + nme.NAME_JOIN_STRING) (constrParams filter matchesName) match { - case Nil => assert(false, name + " not in " + constrParams) ; null + case Nil => abort(name + " not in " + constrParams) case p :: _ => p } } @@ -281,7 +281,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { specializedStats find { case Assign(sel @ Select(This(_), _), rhs) => ( (sel.symbol hasFlag SPECIALIZED) - && (nme.unspecializedName(nme.localToGetter(sel.symbol.name)) == nme.localToGetter(sym.name)) + && (nme.unspecializedName(nme.localToGetter(sel.symbol.name.toTermName)) == nme.localToGetter(sym.name.toTermName)) ) case _ => false } @@ -399,7 +399,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def addGetter(sym: Symbol): Symbol = { val getr = addAccessor( - sym, nme.getterName(sym.name), getterFlags(sym.flags)) + sym, nme.getterName(sym.name.toTermName), getterFlags(sym.flags)) getr setInfo MethodType(List(), sym.tpe) defBuf += localTyper.typedPos(sym.pos)(DefDef(getr, Select(This(clazz), sym))) getr @@ -408,7 +408,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def addSetter(sym: Symbol): Symbol = { sym setFlag MUTABLE val setr = addAccessor( - sym, nme.getterToSetter(nme.getterName(sym.name)), setterFlags(sym.flags)) + sym, nme.getterToSetter(nme.getterName(sym.name.toTermName)), setterFlags(sym.flags)) setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe) defBuf += localTyper.typed { //util.trace("adding setter def for "+setr) { @@ -511,7 +511,6 @@ abstract class Constructors extends Transform with ast.TreeDSL { sym = closureClass, constrMods = Modifiers(0), vparamss = List(List(outerFieldDef)), - argss = ListOfNil, body = List(applyMethodDef), superPos = impl.pos) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index cfc3d0a377..45bd5cf003 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -437,19 +437,19 @@ abstract class Erasure extends AddInterfaces noclash = false unit.error( if (member.owner == root) member.pos else root.pos, - s"""bridge generated for member ${fulldef(member)} - |which overrides ${fulldef(other)} - |clashes with definition of $what; - |both have erased type ${exitingPostErasure(bridge.tpe)}""".stripMargin) + sm"""bridge generated for member ${fulldef(member)} + |which overrides ${fulldef(other)} + |clashes with definition of $what; + |both have erased type ${exitingPostErasure(bridge.tpe)}""") } for (bc <- root.baseClasses) { if (settings.debug.value) exitingPostErasure(println( - s"""check bridge overrides in $bc - ${bc.info.nonPrivateDecl(bridge.name)} - ${site.memberType(bridge)} - ${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)} - ${(bridge.matchingSymbol(bc, site))}""".stripMargin)) + sm"""check bridge overrides in $bc + |${bc.info.nonPrivateDecl(bridge.name)} + |${site.memberType(bridge)} + |${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)} + |${(bridge.matchingSymbol(bc, site))}""")) def overriddenBy(sym: Symbol) = sym.matchingSymbol(bc, site).alternatives filter (sym => !sym.isBridge) @@ -522,7 +522,7 @@ abstract class Erasure extends AddInterfaces && !exitingErasure((member.tpe <:< other.tpe))) // no static guarantees (TODO: is the subtype test ever true?) import CODE._ - val _false = FALSE_typed + val _false = FALSE val pt = member.tpe.resultType lazy val zero = if (_false.tpe <:< pt) _false @@ -693,7 +693,7 @@ abstract class Erasure extends AddInterfaces adaptToType(unbox(tree, pt), pt) else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { adaptToType(box(tree, pt.toString), pt) - } else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) { + } else if (isMethodTypeWithEmptyParams(tree.tpe)) { // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) @@ -774,16 +774,21 @@ abstract class Erasure extends AddInterfaces else if (!isPrimitiveValueType(qual1.tpe) && isPrimitiveValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) - if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) + def selectFrom(qual: Tree) = treeCopy.Select(tree, qual, name) + + if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) { tree.symbol = NoSymbol - else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) { + selectFrom(qual1) + } else if (isMethodTypeWithEmptyParams(qual1.tpe)) { assert(qual1.symbol.isStable, qual1.symbol); - qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + val applied = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + adaptMember(selectFrom(applied)) } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) { assert(tree.symbol.owner != ArrayClass) - qual1 = cast(qual1, tree.symbol.owner.tpe) + selectFrom(cast(qual1, tree.symbol.owner.tpe)) + } else { + selectFrom(qual1) } - treeCopy.Select(tree, qual1, name) } case SelectFromArray(qual, name, erasure) => var qual1 = typedQualifier(qual) @@ -861,6 +866,11 @@ abstract class Erasure extends AddInterfaces tree1 } } + + private def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { + case MethodType(Nil, _) => true + case _ => false + } } /** The erasure transformer */ diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 521d732664..77e7e013ab 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -184,7 +184,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { def makeExtensionMethodSymbol = { val extensionName = extensionNames(origMeth).head val extensionMeth = ( - companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) + companion.moduleClass.newMethod(extensionName.toTermName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) setAnnotations origMeth.annotations ) companion.info.decls.enter(extensionMeth) @@ -232,12 +232,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = super.transformStats(stats, exprOwner) map { - case md @ ModuleDef(_, _, _) if extensionDefs contains md.symbol => - val defns = extensionDefs(md.symbol).toList map (member => - atOwner(md.symbol)(localTyper.typedPos(md.pos.focus)(member)) - ) - extensionDefs -= md.symbol - deriveModuleDef(md)(tmpl => deriveTemplate(tmpl)(_ ++ defns)) + case md @ ModuleDef(_, _, _) => + val extraStats = extensionDefs remove md.symbol match { + case Some(defns) => defns.toList map (defn => atOwner(md.symbol)(localTyper.typedPos(md.pos.focus)(defn.duplicate))) + case _ => Nil + } + if (extraStats.isEmpty) md + else deriveModuleDef(md)(tmpl => deriveTemplate(tmpl)(_ ++ extraStats)) case stat => stat } diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index a52dadb134..b2602f47de 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -23,7 +23,7 @@ abstract class Flatten extends InfoTransform { val old = (scope lookupUnshadowedEntries sym.name).toList old foreach (scope unlink _) scope enter sym - log(s"lifted ${sym.fullLocationString}" + ( if (old.isEmpty) "" else " after unlinking $old from scope." )) + log(s"lifted ${sym.fullLocationString}" + ( if (old.isEmpty) "" else s" after unlinking $old from scope." )) old } diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 4a23e65ad2..0198f959e3 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -247,8 +247,8 @@ abstract class LambdaLift extends InfoTransform { // SI-5652 If the lifted symbol is accessed from an inner class, it will be made public. (where?) // Generating a a unique name, mangled with the enclosing class name, avoids a VerifyError // in the case that a sub-class happens to lifts out a method with the *same* name. - val name = freshen(sym.name + nme.NAME_JOIN_STRING) - if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) nme.expandedName(name, sym.enclClass) + val name = freshen("" + sym.name + nme.NAME_JOIN_STRING) + if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) nme.expandedName(name.toTermName, sym.enclClass) else name } } @@ -290,7 +290,7 @@ abstract class LambdaLift extends InfoTransform { proxies(owner) = for (fv <- freeValues.toList) yield { val proxyName = proxyNames.getOrElse(fv, fv.name) - val proxy = owner.newValue(proxyName, owner.pos, newFlags) setInfo fv.info + val proxy = owner.newValue(proxyName.toTermName, owner.pos, newFlags) setInfo fv.info if (owner.isClass) owner.info.decls enter proxy proxy } diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 481228fb3d..e6c9afb042 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -68,7 +68,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD curTree = tree tree match { - + case Block(_, _) => val block1 = super.transform(tree) val Block(stats, expr) = block1 @@ -79,7 +79,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD List(stat) }) treeCopy.Block(block1, stats1, expr) - + case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) { val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) { val enclosingClassOrDummyOrMethod = { @@ -100,9 +100,9 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym) sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR) (rhs1, sDef) - } else + } else (transform(rhs), EmptyTree) - + val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) if (slowPathDef != EmptyTree) Block(slowPathDef, ddef1) else ddef1 } @@ -189,10 +189,10 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD case _ => prependStats(bmps, rhs) } } - + def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): Tree = { - val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, STABLE | PRIVATE) + val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE) defSym setInfo MethodType(List(), lzyVal.tpe.resultType) defSym.owner = lzyVal.owner debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal") @@ -201,8 +201,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) DEF(defSym).mkTree(addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) setSymbol defSym } - - + + def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): (Tree, Tree) = { val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal) @@ -221,7 +221,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD * Similarly as for normal lazy val members (see Mixin), the result will be a tree of the form * { if ((bitmap&n & MASK) == 0) this.l$compute() * else l$ - * + * * def l$compute() = { synchronized(enclosing_class_or_dummy) { * if ((bitmap$n & MASK) == 0) { * l$ = <rhs> diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index ac1cdd1f46..45ef083b66 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -207,14 +207,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // println("creating new getter for "+ field +" : "+ field.info +" at "+ field.locationString+(field hasFlag MUTABLE)) val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED | ( if (field.isMutable) 0 else STABLE ) // TODO preserve pre-erasure info? - clazz.newMethod(nme.getterName(field.name), field.pos, newFlags) setInfo MethodType(Nil, field.info) + clazz.newMethod(nme.getterName(field.name.toTermName), field.pos, newFlags) setInfo MethodType(Nil, field.info) } /** Create a new setter. Setters are never private or local. They are * always accessors and deferred. */ def newSetter(field: Symbol): Symbol = { //println("creating new setter for "+field+field.locationString+(field hasFlag MUTABLE)) - val setterName = nme.getterToSetter(nme.getterName(field.name)) + val setterName = nme.getterToSetter(nme.getterName(field.name.toTermName)) val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED val setter = clazz.newMethod(setterName, field.pos, newFlags) // TODO preserve pre-erasure info? @@ -266,7 +266,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** Mix in members of implementation class mixinClass into class clazz */ def mixinImplClassMembers(mixinClass: Symbol, mixinInterface: Symbol) { - assert(mixinClass.isImplClass, "Not an impl class:" + + if (!mixinClass.isImplClass) debugwarn ("Impl class flag is not set " + ((mixinClass.debugLocationString, mixinInterface.debugLocationString))) for (member <- mixinClass.info.decls ; if isForwarded(member)) { @@ -315,7 +315,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // carries over the current entry in the type history) val sym = enteringErasure { // so we have a type history entry before erasure - clazz.newValue(nme.getterToLocal(mixinMember.name), mixinMember.pos).setInfo(mixinMember.tpe.resultType) + clazz.newValue(nme.getterToLocal(mixinMember.name.toTermName), mixinMember.pos).setInfo(mixinMember.tpe.resultType) } sym updateInfo mixinMember.tpe.resultType // info at current phase @@ -357,7 +357,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // first complete the superclass with mixed in members addMixedinMembers(clazz.superClass, unit) - //Console.println("adding members of " + clazz.info.baseClasses.tail.takeWhile(superclazz !=) + " to " + clazz);//DEBUG for (mc <- clazz.mixinClasses ; if mc hasFlag lateINTERFACE) { // @SEAN: adding trait tracking so we don't have to recompile transitive closures unit.depends += mc @@ -381,20 +380,20 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { if (clazz.isImplClass) { clazz setFlag lateMODULE var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName) - if (sourceModule != NoSymbol) { - sourceModule setPos sym.pos - if (sourceModule.flags != MODULE) { - log("!!! Directly setting sourceModule flags from %s to MODULE".format(sourceModule.flagString)) - sourceModule.flags = MODULE - } - } - else { + if (sourceModule == NoSymbol) { sourceModule = ( clazz.owner.newModuleSymbol(sym.name.toTermName, sym.pos, MODULE) setModuleClass sym.asInstanceOf[ClassSymbol] ) clazz.owner.info.decls enter sourceModule } + else { + sourceModule setPos sym.pos + if (sourceModule.flags != MODULE) { + log("!!! Directly setting sourceModule flags from %s to MODULE".format(sourceModule.flagString)) + sourceModule.flags = MODULE + } + } sourceModule setInfo sym.tpe // Companion module isn't visible for anonymous class at this point anyway assert(clazz.sourceModule != NoSymbol || clazz.isAnonymousClass, @@ -513,7 +512,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - create a new method definition that also has a `self` parameter * (which comes first) Iuli: this position is assumed by tail call elimination * on a different receiver. Storing a new 'this' assumes it is located at - * index 0 in the local variable table. See 'STORE_THIS' and GenJVM/GenMSIL. + * index 0 in the local variable table. See 'STORE_THIS' and GenASM. * - Map implementation class types in type-apply's to their interfaces * - Remove all fields in implementation classes */ @@ -711,7 +710,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def bitmapFor(clazz0: Symbol, offset: Int, field: Symbol): Symbol = { val category = bitmapCategory(field) - val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field)) + val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field)).toTermName val sym = clazz0.info.decl(bitmapName) assert(!sym.isOverloaded, sym) @@ -775,7 +774,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Symbol = { - val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, PRIVATE) + val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, PRIVATE) val params = defSym newSyntheticValueParams args.map(_.symbol.tpe) defSym setInfoAndEnter MethodType(params, lzyVal.tpe.resultType) val rhs: Tree = (gen.mkSynchronizedCheck(attrThis, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) @@ -868,8 +867,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val cond = Apply(Select(moduleVarRef, Object_eq), List(NULL)) mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args) case _ => - assert(false, "Invalid getter " + rhs + " for module in class " + clazz) - EmptyTree + abort(s"Invalid getter $rhs for module in $clazz") } def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { @@ -877,7 +875,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val bitmapSym = bitmapFor(clazz, offset, sym) val kind = bitmapKind(sym) val mask = maskForOffset(offset, sym, kind) - val msg = "Uninitialized field: " + unit.source + ": " + pos.line + val msg = s"Uninitialized field: ${unit.source}: ${pos.line}" val result = IF (mkTest(clazz, mask, bitmapSym, false, kind)) . THEN (retVal) . @@ -1037,16 +1035,17 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } // if class is not a trait add accessor definitions else if (!clazz.isTrait) { + // This needs to be a def to avoid sharing trees + def accessedRef = accessedReference(sym) if (sym.hasAccessorFlag && (!sym.isDeferred || sym.hasFlag(lateDEFERRED))) { // add accessor definitions addDefDef(sym, { - val accessedRef = accessedReference(sym) if (sym.isSetter) { if (isOverriddenSetter(sym)) UNIT else accessedRef match { - case Literal(_) => accessedRef - case _ => - val init = Assign(accessedRef, Ident(sym.firstParam)) + case ref @ Literal(_) => ref + case ref => + val init = Assign(ref, Ident(sym.firstParam)) val getter = sym.getter(clazz) if (!needsInitFlag(getter)) init @@ -1061,11 +1060,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) { // add modules - val vdef = gen.mkModuleVarDef(sym) - addDef(position(sym), vdef) + val vsym = sym.owner.newModuleVarSymbol(sym) + addDef(position(sym), ValDef(vsym)) - val rhs = gen.newModule(sym, vdef.symbol.tpe) - val assignAndRet = gen.mkAssignAndReturn(vdef.symbol, rhs) + // !!! TODO - unravel the enormous duplication between this code and + // eliminateModuleDefs in RefChecks. + val rhs = gen.newModule(sym, vsym.tpe) + val assignAndRet = gen.mkAssignAndReturn(vsym, rhs) val attrThis = gen.mkAttributedThis(clazz) val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet, sym, List()) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 2574644727..4e4c1b98ac 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -323,11 +323,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = { if (nme.INITIALIZER == name || (types1.isEmpty && types2.isEmpty)) - name + name.toTermName else if (nme.isSetterName(name)) - nme.getterToSetter(specializedName(nme.setterToGetter(name), types1, types2)) + nme.getterToSetter(specializedName(nme.setterToGetter(name.toTermName), types1, types2)) else if (nme.isLocalName(name)) - nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2)) + nme.getterToLocal(specializedName(nme.localToGetter(name.toTermName), types1, types2)) else { val (base, cs, ms) = nme.splitSpecializedName(name) newTermName(base.toString + "$" @@ -689,7 +689,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def mkAccessor(field: Symbol, name: Name) = { val newFlags = (SPECIALIZED | m.getter(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR) // we rely on the super class to initialize param accessors - val sym = sClass.newMethod(name, field.pos, newFlags) + val sym = sClass.newMethod(name.toTermName, field.pos, newFlags) info(sym) = SpecializedAccessor(field) sym } @@ -708,7 +708,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // debuglog("m: " + m + " isLocal: " + nme.isLocalName(m.name) + " specVal: " + specVal.name + " isLocal: " + nme.isLocalName(specVal.name)) if (nme.isLocalName(m.name)) { - val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name)) setInfo MethodType(Nil, specVal.info) + val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name.toTermName)) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getter(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 6ab99eaec6..c375bc4362 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -149,7 +149,7 @@ abstract class TailCalls extends Transform { def enclosingType = method.enclClass.typeOfThis def isEligible = method.isEffectivelyFinal // @tailrec annotation indicates mandatory transformation - def isMandatory = method.hasAnnotation(TailrecClass) && !forMSIL + def isMandatory = method.hasAnnotation(TailrecClass) def isTransformed = isEligible && accessed(label) def tailrecFailure() = unit.error(failPos, "could not optimize @tailrec annotated " + method + ": " + failReason) @@ -229,7 +229,6 @@ abstract class TailCalls extends Transform { } else if (!matchesTypeArgs) failHere("it is called recursively with different type arguments") else if (receiver == EmptyTree) rewriteTailCall(This(currentClass)) - else if (forMSIL) fail("it cannot be optimized on MSIL") else if (!receiverIsSame) failHere("it changes type of 'this' on a polymorphic recursive call") else rewriteTailCall(receiver) } @@ -326,8 +325,16 @@ abstract class TailCalls extends Transform { transformTrees(cases).asInstanceOf[List[CaseDef]] ) + case Try(block, catches, finalizer @ EmptyTree) => + // SI-1672 Catches are in tail position when there is no finalizer + treeCopy.Try(tree, + noTailTransform(block), + transformTrees(catches).asInstanceOf[List[CaseDef]], + EmptyTree + ) + case Try(block, catches, finalizer) => - // no calls inside a try are in tail position, but keep recursing for nested functions + // no calls inside a try are in tail position if there is a finalizer, but keep recursing for nested functions treeCopy.Try(tree, noTailTransform(block), noTailTransforms(catches).asInstanceOf[List[CaseDef]], diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 4f889a1d86..90ea6c94d8 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -61,24 +61,6 @@ abstract class UnCurry extends InfoTransform // uncurry and uncurryType expand type aliases - /** Traverse tree omitting local method definitions. - * If a `return` is encountered, set `returnFound` to true. - * Used for MSIL only. - */ - private object lookForReturns extends Traverser { - var returnFound = false - override def traverse(tree: Tree): Unit = tree match { - case Return(_) => returnFound = true - case DefDef(_, _, _, _, _, _) => ; - case _ => super.traverse(tree) - } - def found(tree: Tree) = { - returnFound = false - traverse(tree) - returnFound - } - } - class UnCurryTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { private var needTryLift = false private var inPattern = false @@ -268,7 +250,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } @@ -371,7 +353,7 @@ abstract class UnCurry extends InfoTransform } val isDefinedAtMethodDef = { - val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL) + val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL | SYNTHETIC) val params = methSym newSyntheticValueParams formals methSym setInfoAndEnter MethodType(params, BooleanClass.tpe) @@ -380,11 +362,11 @@ abstract class UnCurry extends InfoTransform val body = bodyForIDA match { case Match(selector, cases) => - if (cases exists treeInfo.isDefaultCase) TRUE_typed + if (cases exists treeInfo.isDefaultCase) TRUE else doSubst(Match(/*gen.mkUnchecked*/(selector), - (cases map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ ( - DEFAULT ==> FALSE_typed))) + (cases map (c => deriveCaseDef(c)(x => TRUE))) :+ ( + DEFAULT ==> FALSE))) } body.changeOwner(fun.symbol -> methSym) @@ -394,7 +376,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } @@ -537,13 +519,6 @@ abstract class UnCurry extends InfoTransform finally needTryLift = saved } - /** A try or synchronized needs to be lifted anyway for MSIL if it contains - * return statements. These are disallowed in the CLR. By lifting - * such returns will be converted to throws. - */ - def shouldBeLiftedAnyway(tree: Tree) = false && // buggy, see #1981 - forMSIL && lookForReturns.found(tree) - /** Transform tree `t` to { def f = t; f } where `f` is a fresh name */ def liftTree(tree: Tree) = { @@ -618,13 +593,10 @@ abstract class UnCurry extends InfoTransform treeCopy.UnApply(tree, fn1, args1) case Apply(fn, args) => - if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) - transform(treeCopy.Apply(tree, fn, List(liftTree(args.head)))) - else - withNeedLift(true) { - val formals = fn.tpe.paramTypes - treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) - } + withNeedLift(true) { + val formals = fn.tpe.paramTypes + treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals))) + } case Assign(_: RefTree, _) => withNeedLift(true) { super.transform(tree) } @@ -643,7 +615,7 @@ abstract class UnCurry extends InfoTransform super.transform(tree) case Try(block, catches, finalizer) => - if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) + if (needTryLift) transform(liftTree(tree)) else super.transform(tree) case CaseDef(pat, guard, body) => @@ -668,7 +640,7 @@ abstract class UnCurry extends InfoTransform tree1 } ) - assert(result.tpe != null, result + " tpe is null") + assert(result.tpe != null, result.shortClass + " tpe is null:\n" + result) result setType uncurryTreeType(result.tpe) } @@ -835,7 +807,7 @@ abstract class UnCurry extends InfoTransform } // create the symbol - val forwsym = currentClass.newMethod(dd.name, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype + val forwsym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype // create the tree val forwtree = theTyper.typedPos(dd.pos) { diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index bfc9f08553..30c12a4286 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -106,9 +106,9 @@ trait ContextErrors { s"$name extends Any, not AnyRef" ) if (isPrimitiveValueType(found) || isTrivialTopType(tp)) "" else "\n" + - s"""|Note that $what. - |Such types can participate in value classes, but instances - |cannot appear in singleton types or in reference comparisons.""".stripMargin + sm"""|Note that $what. + |Such types can participate in value classes, but instances + |cannot appear in singleton types or in reference comparisons.""" } import ErrorUtils._ @@ -182,14 +182,18 @@ trait ContextErrors { } def ParentTypesError(templ: Template, ex: TypeError) = { - templ.tpe = null - issueNormalTypeError(templ, ex.getMessage()) + templ.tpe = null + issueNormalTypeError(templ, ex.getMessage()) + setError(templ) } // additional parentTypes errors - def ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) = + def ConstrArgsInParentWhichIsTraitError(arg: Tree, parent: Symbol) = issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments") + def ConstrArgsInParentOfTraitError(arg: Tree, parent: Symbol) = + issueNormalTypeError(arg, "parents of traits may not have parameters") + def MissingTypeArgumentsParentTpeError(supertpt: Tree) = issueNormalTypeError(supertpt, "missing type arguments") @@ -511,7 +515,7 @@ trait ContextErrors { NormalTypeError(tree, fun.tpe+" does not take parameters") // Dynamic - def DynamicVarArgUnsupported(tree: Tree, name: String) = + def DynamicVarArgUnsupported(tree: Tree, name: Name) = issueNormalTypeError(tree, name+ " does not support passing a vararg parameter") def DynamicRewriteError(tree: Tree, err: AbsTypeError) = { @@ -1040,9 +1044,6 @@ trait ContextErrors { def MaxParametersCaseClassError(tree: Tree) = issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") - def InheritsItselfError(tree: Tree) = - issueNormalTypeError(tree, tree.tpe.typeSymbol+" inherits itself") - def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") @@ -1125,9 +1126,9 @@ trait ContextErrors { (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { def coreMsg = - s"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} - | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} - | $trailer""".stripMargin + sm"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} + | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} + | $trailer""" def viewMsg = { val found :: req :: _ = pt.typeArgs def explanation = { @@ -1138,19 +1139,19 @@ trait ContextErrors { // involving Any, are further explained from foundReqMsg. if (AnyRefClass.tpe <:< req) ( if (sym == AnyClass || sym == UnitClass) ( - s"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely - |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""".stripMargin + sm"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely + |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""" ) else boxedClass get sym map (boxed => - s"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but - |methods inherited from Object are rendered ambiguous. This is to avoid - |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. - |You may wish to use a type ascription: `x: ${boxed.fullName}`.""".stripMargin + sm"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but + |methods inherited from Object are rendered ambiguous. This is to avoid + |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. + |You may wish to use a type ascription: `x: ${boxed.fullName}`.""" ) getOrElse "" ) else - s"""|Note that implicit conversions are not applicable because they are ambiguous: - |${coreMsg}are possible conversion functions from $found to $req""".stripMargin + sm"""|Note that implicit conversions are not applicable because they are ambiguous: + |${coreMsg}are possible conversion functions from $found to $req""" } typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + ( if (explanation == "") "" else "\n" + explanation @@ -1276,7 +1277,10 @@ trait ContextErrors { fail() } - private def implRefError(message: String) = genericError(methPart(macroDdef.rhs), message) + private def implRefError(message: String) = { + val treeInfo.Applied(implRef, _, _) = macroDdef.rhs + genericError(implRef, message) + } private def compatibilityError(message: String) = implRefError( diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 1af61d31ec..c0d2f44c7b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -996,7 +996,14 @@ trait Contexts { self: Analyzer => if (settings.lint.value && selectors.nonEmpty && result != NoSymbol && pos != NoPosition) recordUsage(current, result) - result + // Harden against the fallout from bugs like SI-6745 + // + // [JZ] I considered issuing a devWarning and moving the + // check inside the above loop, as I believe that + // this always represents a mistake on the part of + // the caller. + if (definitions isImportable result) result + else NoSymbol } private def selectorString(s: ImportSelector): String = { if (s.name == nme.WILDCARD && s.rename == null) "_" diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index eb45f9b847..bf5aa95f22 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -149,9 +149,20 @@ trait Implicits { class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter) { override def toString = "SearchResult(%s, %s)".format(tree, if (subst.isEmpty) "" else subst) + + def isFailure = false + def isAmbiguousFailure = false + final def isSuccess = !isFailure + } + + lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { + override def isFailure = true } - lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) + lazy val AmbiguousSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { + override def isFailure = true + override def isAmbiguousFailure = true + } /** A class that records an available implicit * @param name The name of the implicit @@ -823,7 +834,7 @@ trait Implicits { catch divergenceHandler tryImplicitInfo(i) match { - case SearchFailure => + case sr if sr.isFailure => // We don't want errors that occur during checking implicit info // to influence the check of further infos. context.condBufferFlush(_.kind != ErrorKinds.Divergent) @@ -863,14 +874,14 @@ trait Implicits { rest find (alt => !improves(chosen, alt)) match { case Some(competing) => AmbiguousImplicitError(chosen, competing, "both", "and", "")(isView, pt, tree)(context) - return SearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala + return AmbiguousSearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala case _ => if (isView) chosen.useCountView += 1 else chosen.useCountArg += 1 } } - if (best == SearchFailure) { + if (best.isFailure) { /** If there is no winner, and we witnessed and caught divergence, * now we can throw it for the error message. */ @@ -1298,17 +1309,17 @@ trait Implicits { else { if (ReflectRuntimeUniverse == NoSymbol) { // todo. write a test for this - context.error(pos, s""" - |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. - |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. - |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin) + context.error(pos, + sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. + |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""") return SearchFailure } if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) { - context.error(pos, s""" - |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. - |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. - |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""".trim.stripMargin) + context.error(pos, + sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. + |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. + |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""") return SearchFailure } val cm = typed(Ident(ReflectRuntimeCurrentMirror)) @@ -1369,24 +1380,25 @@ trait Implicits { var result = searchImplicit(context.implicitss, true) - if (result == SearchFailure) { + if (result.isFailure) { if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart) } else { if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart) if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) } - if (result == SearchFailure) { + if (result.isFailure) { val previousErrs = context.flushAndReturnBuffer() val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null + val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits result = materializeImplicit(pt) // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? - if (result == SearchFailure) result = searchImplicit(implicitsOfExpectedType, false) + if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, false) - if (result == SearchFailure) { + if (result.isFailure) { context.updateBuffer(previousErrs) if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { @@ -1395,7 +1407,7 @@ trait Implicits { } } - if (result == SearchFailure && settings.debug.value) + if (result.isFailure && settings.debug.value) log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index be3c027259..6cc536ad9b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -220,7 +220,7 @@ trait Infer extends Checkable { // such as T <: T gets completed. See #360 tvar.constr.inst = ErrorType else - assert(false, tvar.origin+" at "+tvar.origin.typeSymbol.owner) + abort(tvar.origin+" at "+tvar.origin.typeSymbol.owner) } tvars map instantiate } diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 4b534b0d2e..4d1ab98fa0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -117,16 +117,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } def pickle(macroImplRef: Tree): Tree = { - val macroImpl = macroImplRef.symbol + val MacroImplReference(owner, macroImpl, targs) = macroImplRef val paramss = macroImpl.paramss - // this logic relies on the assumptions that were valid for the old macro prototype - // namely that macro implementations can only be defined in top-level classes and modules - // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different - // for example, a macro def could be defined in a trait that is implemented by an object - // there are some more clever cases when seemingly non-static method ends up being statically accessible - // however, the code below doesn't account for these guys, because it'd take a look of time to get it right - // for now I leave it as a todo and move along to more the important stuff // todo. refactor when fixing SI-5498 def className: String = { def loop(sym: Symbol): String = sym match { @@ -138,7 +131,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { loop(sym.owner) + separator + sym.javaSimpleName.toString } - loop(macroImpl.owner.enclClass) + loop(owner) } def signature: List[Int] = { @@ -159,7 +152,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // I just named it "macro", because it's macro-related, but I could as well name it "foobar" val nucleus = Ident(newTermName("macro")) val wrapped = Apply(nucleus, payload map { case (k, v) => Assign(pickleAtom(k), pickleAtom(v)) }) - val pickle = gen.mkTypeApply(wrapped, treeInfo.typeArguments(macroImplRef.duplicate)) + val pickle = gen.mkTypeApply(wrapped, targs map (_.duplicate)) // assign NoType to all freshly created AST nodes // otherwise pickler will choke on tree.tpe being null @@ -324,7 +317,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { def sigma(tpe: Type): Type = SigmaTypeMap(tpe) def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = - macroDef.newValueParameter(name, pos, flags) setInfo tpe + macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe def implType(isType: Boolean, origTpe: Type): Type = { def tsym = if (isType) WeakTagClass else ExprClass def targ = origTpe.typeArgs.headOption getOrElse NoType @@ -477,9 +470,6 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * Is also capable of detecting REPL and reusing its classloader. */ lazy val macroClassloader: ClassLoader = { - if (global.forMSIL) - throw new UnsupportedOperationException("Scala reflection not available on this platform") - val classpath = global.classPath.asURLs macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath)) val loader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 3ac5d388d3..d74d5ecfbe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -50,7 +50,9 @@ trait MethodSynthesis { class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { def mkThis = This(clazz) setPos clazz.pos.focus - def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(Select(mkThis, sym)) + def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)( + if (clazz.isClass) Select(This(clazz), sym) else Ident(sym) + ) private def isOverride(name: TermName) = clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) @@ -60,7 +62,7 @@ trait MethodSynthesis { overrideFlag | SYNTHETIC } def newMethodFlags(method: Symbol) = { - val overrideFlag = if (isOverride(method.name)) OVERRIDE else 0L + val overrideFlag = if (isOverride(method.name.toTermName)) OVERRIDE else 0L (method.flags | overrideFlag | SYNTHETIC) & ~DEFERRED } @@ -68,11 +70,13 @@ trait MethodSynthesis { localTyper typed ValOrDefDef(method, f(method)) private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + val name1 = name.toTermName + val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1)) finishMethod(m setInfoAndEnter info, f) } private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + val name1 = name.toTermName + val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1)) finishMethod(m setInfoAndEnter infoFn(m), f) } private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = { @@ -201,8 +205,7 @@ trait MethodSynthesis { ) def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil - if (forMSIL) Nil - else if (vd.symbol hasAnnotation BeanPropertyAttr) + if (vd.symbol hasAnnotation BeanPropertyAttr) BeanGetter(vd) :: setter else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr) BooleanBeanGetter(vd) :: setter @@ -521,9 +524,6 @@ trait MethodSynthesis { } protected def enterBeans(tree: ValDef) { - if (forMSIL) - return - val ValDef(mods, name, _, _) = tree val beans = beanAccessorsFromNames(tree) if (beans.nonEmpty) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 79fc0e0081..9a32747c3a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -300,11 +300,11 @@ trait Namers extends MethodSynthesis { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => owner.newConstructor(pos, flags) case DefDef(_, _, _, _, _, _) => owner.newMethod(name.toTermName, pos, flags) case ClassDef(_, _, _, _) => owner.newClassSymbol(name.toTypeName, pos, flags) - case ModuleDef(_, _, _) => owner.newModule(name, pos, flags) + case ModuleDef(_, _, _) => owner.newModule(name.toTermName, pos, flags) case PackageDef(pid, _) => createPackageSymbol(pos, pid) case ValDef(_, _, _, _) => - if (isParameter) owner.newValueParameter(name, pos, flags) - else owner.newValue(name, pos, flags) + if (isParameter) owner.newValueParameter(name.toTermName, pos, flags) + else owner.newValue(name.toTermName, pos, flags) } } private def createFieldSymbol(tree: ValDef): TermSymbol = @@ -874,13 +874,8 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner def checkParent(tpt: Tree): Type = { - val tp = tpt.tpe - val inheritsSelf = tp.typeSymbol == owner - if (inheritsSelf) - InheritsItselfError(tpt) - - if (inheritsSelf || tp.isError) AnyRefClass.tpe - else tp + if (tpt.tpe.isError) AnyRefClass.tpe + else tpt.tpe } val parents = typer.parentTypes(templ) map checkParent @@ -1285,8 +1280,12 @@ trait Namers extends MethodSynthesis { if (!annotated.isInitialized) tree match { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => - // need to be lazy, #1782. enteringTyper to allow inferView in annotation args, SI-5892. - AnnotationInfo lazily enteringTyper(typer typedAnnotation ann) + // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. + AnnotationInfo lazily { + val context1 = typer.context.make(ann) + context1.setReportErrors() + enteringTyper(newTyper(context1) typedAnnotation ann) + } } if (ainfos.nonEmpty) { annotated setAnnotations ainfos diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index c5245c4e9e..ede117f51a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -1146,12 +1146,12 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val expectedOuter = expectedTp.prefix match { case ThisType(clazz) => THIS(clazz) case pre if pre != NoType => REF(pre.prefix, pre.termSymbol) - case _ => TRUE_typed // fallback for SI-6183 + case _ => TRUE // fallback for SI-6183 } // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC | ARTIFACT + val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter } @@ -1278,10 +1278,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // one alternative may still generate multiple trees (e.g., an extractor call + equality test) // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers val combinedAlts = altss map (altTreeMakers => - ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE_typed)))(casegen)) + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) ) - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE_typed)) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE)) codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) } } @@ -1413,7 +1413,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // assert(owner ne null); assert(owner ne NoSymbol) def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = - NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo tp + NoSymbol.newTermSymbol(freshName(prefix), pos, newFlags = SYNTHETIC) setInfo tp def newSynthCaseLabel(name: String) = NoSymbol.newLabel(freshName(name), NoPosition) setFlag treeInfo.SYNTH_CASE_FLAGS @@ -3241,7 +3241,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - private val defaultLabel: Symbol = newSynthCaseLabel("default") + private val defaultLabel: Symbol = newSynthCaseLabel("default") /** Collapse guarded cases that switch on the same constant (the last case may be unguarded). * @@ -3600,7 +3600,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = newSynthCaseLabel("matchEnd") - val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, newFlags = SYNTHETIC) setInfo restpe.withoutAnnotations matchEnd setInfo MethodType(List(matchRes), restpe) def newCaseSym = newSynthCaseLabel("case") setInfo MethodType(Nil, restpe) @@ -3682,7 +3682,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = ifThenElseZero(cond, BLOCK( - condSym === TRUE_typed, + condSym === TRUE, nextBinder === res, next )) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7a7c7c7d25..396c6acd38 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1116,8 +1116,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass - // test is behind a platform guard - def isJavaNumber(s: Symbol) = !forMSIL && (s isSubClass JavaNumberClass) + def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass // includes java.lang.Number if appropriate [SI-5779] def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) @@ -1160,7 +1159,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans nonSensiblyNeq() } else if (isNumeric(receiver)) { - if (!isNumeric(actual) && !forMSIL) + if (!isNumeric(actual)) if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" nonSensiblyNeq() } @@ -1252,55 +1251,61 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans finally popLevel() } - /** Eliminate ModuleDefs. - * - A top level object is replaced with their module class. - * - An inner object is transformed into a module var, created on first access. + /** Eliminate ModuleDefs. In all cases the ModuleDef (carrying a module symbol) is + * replaced with a ClassDef (carrying the corresponding module class symbol) with additional + * trees created as follows: * - * In both cases, this transformation returns the list of replacement trees: - * - Top level: the module class accessor definition - * - Inner: a class definition, declaration of module var, and module var accessor + * 1) A statically reachable object (either top-level or nested only in objects) receives + * no additional trees. + * 2) An inner object which matches an existing member (e.g. implements an interface) + * receives an accessor DefDef to implement the interface. + * 3) An inner object otherwise receives a private ValDef which declares a module var + * (the field which holds the module class - it has a name like Foo$module) and an + * accessor for that field. The instance is created lazily, on first access. */ - private def eliminateModuleDefs(tree: Tree): List[Tree] = { - val ModuleDef(mods, name, impl) = tree - val sym = tree.symbol - val classSym = sym.moduleClass - val cdef = ClassDef(mods | MODULE, name.toTypeName, Nil, impl) setSymbol classSym setType NoType - - def findOrCreateModuleVar() = localTyper.typedPos(tree.pos) { - lazy val createModuleVar = gen.mkModuleVarDef(sym) - sym.enclClass.info.decl(nme.moduleVarName(sym.name.toTermName)) match { - // In case we are dealing with local symbol then we already have - // to correct error with forward reference - case NoSymbol => createModuleVar - case vsym => ValDef(vsym) - } - } - def createStaticModuleAccessor() = exitingRefchecks { - val method = ( - sym.owner.newMethod(sym.name.toTermName, sym.pos, (sym.flags | STABLE) & ~MODULE) - setInfoAndEnter NullaryMethodType(sym.moduleClass.tpe) + private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks { + val ModuleDef(mods, name, impl) = moduleDef + val module = moduleDef.symbol + val site = module.owner + val moduleName = module.name.toTermName + // The typer doesn't take kindly to seeing this ClassDef; we have to + // set NoType so it will be ignored. + val cdef = ClassDef(module.moduleClass, impl) setType NoType + + // Create the module var unless the immediate owner is a class and + // the module var already exists there. See SI-5012, SI-6712. + def findOrCreateModuleVar() = { + val vsym = ( + if (site.isTerm) NoSymbol + else site.info decl nme.moduleVarName(moduleName) ) - localTyper.typedPos(tree.pos)(gen.mkModuleAccessDef(method, sym)) + vsym orElse (site newModuleVarSymbol module) } - def createInnerModuleAccessor(vdef: Tree) = List( - vdef, - localTyper.typedPos(tree.pos) { - val vsym = vdef.symbol - exitingRefchecks { - val rhs = gen.newModule(sym, vsym.tpe) - val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs) - DefDef(sym, body.changeOwner(vsym -> sym)) - } - } - ) - transformTrees(cdef :: { - if (!sym.isStatic) - createInnerModuleAccessor(findOrCreateModuleVar) - else if (sym.isOverridingSymbol) - List(createStaticModuleAccessor()) + def newInnerObject() = { + // Create the module var unless it is already in the module owner's scope. + // The lookup is on module.enclClass and not module.owner lest there be a + // nullary method between us and the class; see SI-5012. + val moduleVar = findOrCreateModuleVar() + val rhs = gen.newModule(module, moduleVar.tpe) + val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs) + val accessor = DefDef(module, body.changeOwner(moduleVar -> module)) + + ValDef(moduleVar) :: accessor :: Nil + } + def matchingInnerObject() = { + val newFlags = (module.flags | STABLE) & ~MODULE + val newInfo = NullaryMethodType(module.moduleClass.tpe) + val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo + + DefDef(accessor, Select(This(site), module)) :: Nil + } + val newTrees = cdef :: ( + if (module.isStatic) + if (module.isOverridingSymbol) matchingInnerObject() else Nil else - Nil - }) + newInnerObject() + ) + transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { @@ -1382,8 +1387,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkCompileTimeOnly(sym: Symbol, pos: Position) = { if (sym.isCompileTimeOnly) { def defaultMsg = - s"""|Reference to ${sym.fullLocationString} should not have survived past type checking, - |it should have been processed and eliminated during expansion of an enclosing macro.""".stripMargin + sm"""Reference to ${sym.fullLocationString} should not have survived past type checking, + |it should have been processed and eliminated during expansion of an enclosing macro.""" // The getOrElse part should never happen, it's just here as a backstop. unit.error(pos, sym.compileTimeOnlyMessage getOrElse defaultMsg) } @@ -1680,7 +1685,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkAnyValSubclass(currentOwner) if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree - case dc@TypeTreeWithDeferredRefCheck() => assert(false, "adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc"); dc + case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") case tpt@TypeTree() => if(tpt.original != null) { tpt.original foreach { diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 64c5b41638..0a1d3bfa7a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -4,7 +4,31 @@ package typechecker trait StdAttachments { self: Analyzer => + import global._ + + /** Carries information necessary to expand the host tree. + * At times we need to store this info, because macro expansion can be delayed until its targs are inferred. + * After a macro application has been successfully expanded, this attachment is destroyed. + */ type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) + + /** After being synthesized by the parser, primary constructors aren't fully baked yet. + * A call to super in such constructors is just a fill-me-in-later dummy resolved later + * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and + * allows them to complete the synthesis. + */ + case class SuperCallArgsAttachment(argss: List[List[Tree]]) + + /** Convenience method for `SuperCallArgsAttachment`. + * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, + * so it really benefits from a dedicated extractor. + */ + def superCallArgs(tree: Tree): Option[List[List[Tree]]] = + tree.attachments.get[SuperCallArgsAttachment] collect { case SuperCallArgsAttachment(argss) => argss } + + /** Determines whether the given tree has an associated SuperCallArgsAttachment. + */ + def hasSuperArgs(tree: Tree): Boolean = superCallArgs(tree).nonEmpty }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 5fb9a5e67e..0992cd7955 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -525,8 +525,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } def isJavaProtected = host.isTrait && sym.isJavaDefined && { restrictionError(pos, unit, - s"""|$clazz accesses protected $sym inside a concrete trait method. - |Add an accessor in a class extending ${sym.enclClass} as a workaround.""".stripMargin + sm"""$clazz accesses protected $sym inside a concrete trait method. + |Add an accessor in a class extending ${sym.enclClass} as a workaround.""" ) true } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index be7554abe2..fb95c952d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -268,11 +268,9 @@ abstract class TreeCheckers extends Analyzer { if (sym.owner != currentOwner) { val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse { fail("DefTree can't find owner: ") ; NoSymbol } if (sym.owner != expected) - fail("""| - | currentOwner chain: %s - | symbol chain: %s""".stripMargin.format( - currentOwner.ownerChain take 3 mkString " -> ", - sym.ownerChain mkString " -> ") + fail(sm"""| + | currentOwner chain: ${currentOwner.ownerChain take 3 mkString " -> "} + | symbol chain: ${sym.ownerChain mkString " -> "}""" ) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 19f0b56e94..3bb6ae53dc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -480,7 +480,7 @@ trait TypeDiagnostics { && (m.isPrivate || m.isLocal) && !targets(m) && !(m.name == nme.WILDCARD) // e.g. val _ = foo - && !ignoreNames(m.name) // serialization methods + && !ignoreNames(m.name.toTermName) // serialization methods && !isConstantType(m.info.resultType) // subject to constant inlining && !treeTypes.exists(_ contains m) // e.g. val a = new Foo ; new a.Bar ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 42c7d4a6b8..a1c1b53cce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -52,8 +52,10 @@ trait Typers extends Modes with Adaptations with Tags { object UnTyper extends Traverser { override def traverse(tree: Tree) = { - if (tree != EmptyTree) tree.tpe = null - if (tree.hasSymbolField) tree.symbol = NoSymbol + if (tree.canHaveAttrs) { + tree.tpe = null + if (tree.hasSymbolField) tree.symbol = NoSymbol + } super.traverse(tree) } } @@ -135,7 +137,7 @@ trait Typers extends Modes with Adaptations with Tags { val res = if (paramFailed || (paramTp.isError && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, false, context) argResultsBuff += res - if (res != SearchFailure) { + if (res.isSuccess) { argBuff += mkArg(res.tree, param.name) } else { mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args @@ -711,7 +713,7 @@ trait Typers extends Modes with Adaptations with Tags { featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name def action(): Boolean = { - def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context) != SearchFailure + def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context).isSuccess def hasOption = settings.language.value exists (s => s == featureName || s == "_") val OK = hasImport || hasOption if (!OK) { @@ -1130,13 +1132,6 @@ trait Typers extends Modes with Adaptations with Tags { // (14); the condition prevents chains of views debuglog("inferring view from " + tree.tpe + " to " + pt) val coercion = inferView(tree, tree.tpe, pt, true) - // convert forward views of delegate types into closures wrapped around - // the delegate's apply method (the "Invoke" method, which was translated into apply) - if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) { - val meth: Symbol = tree.tpe.member(nme.apply) - debuglog("replacing forward delegate view with: " + meth + ":" + meth.tpe) - return typed(Select(tree, meth), mode, pt) - } if (coercion != EmptyTree) { def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe if (settings.logImplicitConv.value) @@ -1327,13 +1322,6 @@ trait Typers extends Modes with Adaptations with Tags { if (member(qual, name) != NoSymbol) qual else adaptToMember(qual, HasMember(name)) - private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = { - // XXX: see about using the class's symbol.... - enclTparams foreach (sym => context.scope.enter(sym)) - namer.enterValueParams(vparamss) - typed(cbody) - } - private def validateNoCaseAncestor(clazz: Symbol) = { if (!phase.erasedTypes) { for (ancestor <- clazz.ancestors find (_.isCase)) { @@ -1435,114 +1423,242 @@ trait Typers extends Modes with Adaptations with Tags { unit.error(tparam.pos, "type parameter of value class may not be specialized") } - def parentTypes(templ: Template): List[Tree] = - if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) - else try { - val clazz = context.owner - // Normalize supertype and mixins so that supertype is always a class, not a trait. - var supertpt = typedTypeConstructor(templ.parents.head) - val firstParent = supertpt.tpe.typeSymbol - var mixins = templ.parents.tail map typedType - // If first parent is a trait, make it first mixin and add its superclass as first parent - while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) { - val supertpt1 = typedType(supertpt) - if (!supertpt1.isErrorTyped) { - mixins = supertpt1 :: mixins - supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus - } + /** Typechecks a parent type reference. + * + * This typecheck is harder than it might look, because it should honor early + * definitions and also perform type argument inference with the help of super call + * arguments provided in `encodedtpt`. + * + * The method is called in batches (batch = 1 time per each parent type referenced), + * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef + * and once from typer, when typechecking the definition. + * + * ***Arguments*** + * + * `encodedtpt` represents the parent type reference wrapped in an `Apply` node + * which indicates value arguments (i.e. type macro arguments or super constructor call arguments) + * If no value arguments are provided by the user, the `Apply` node is still + * there, but its `args` will be set to `Nil`. + * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`. + * + * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser. + * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call + * (I wrote "maybe" because trait constructors don't call super constructors). + * This argument is synthesized by `tools.nsc.ast.Trees.Template`. + * + * `inMixinPosition` indicates whether the reference is not the first in the + * list of parents (and therefore cannot be a class) or the opposite. + * + * ***Return value and side effects*** + * + * Returns a `TypeTree` representing a resolved parent type. + * If the typechecked parent reference implies non-nullary and non-empty argument list, + * this argument list is attached to the returned value in SuperCallArgsAttachment. + * The attachment is necessary for the subsequent typecheck to fixup a super constructor call + * in the body of the primary constructor (see `typedTemplate` for details). + * + * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects + * described in the docs of that method. It might also attribute the Super(_, _) reference + * (if present) inside the primary constructor of `templ`. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * this method will be called six times: + * + * (3 times from the namer) + * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false) + * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true) + * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true) + * + * (3 times from the typer) + * <the same three calls> + */ + private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = { + val app = treeInfo.dissectApplied(encodedtpt) + val (treeInfo.Applied(core, targs, argss), decodedtpt) = (app, app.callee) + val argssAreTrivial = argss == Nil || argss == ListOfNil + + // we cannot avoid cyclic references with `initialize` here, because when type macros arrive, + // we'll have to check the probe for isTypeMacro anyways. + // therefore I think it's reasonable to trade a more specific "inherits itself" error + // for a generic, yet understandable "cyclic reference" error + var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol + if (probe == null) probe = NoSymbol + probe.initialize + + if (probe.isTrait || inMixinPosition) { + if (!argssAreTrivial) { + if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe) + else () // a class in a mixin position - this warrants an error in `validateParentClasses` + // therefore here we do nothing, e.g. don't check that the # of ctor arguments + // matches the # of ctor parameters or stuff like that } - if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait) - supertpt.tpe = AnyRefClass.tpe - - // Determine - // - supertparams: Missing type parameters from supertype - // - supertpe: Given supertype, polymorphic in supertparams - val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else List() - var supertpe = supertpt.tpe - if (!supertparams.isEmpty) - supertpe = PolyType(supertparams, appliedType(supertpe.typeConstructor, supertparams map (_.tpeHK))) - - // A method to replace a super reference by a New in a supercall - def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match { - case Apply(fn, args) => - treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate)) - case Select(Super(_, _), nme.CONSTRUCTOR) => - treeCopy.Select( - scall, - atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe), - nme.CONSTRUCTOR) + typedType(decodedtpt) + } else { + var supertpt = typedTypeConstructor(decodedtpt) + val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else Nil + if (supertparams.nonEmpty) { + typedPrimaryConstrBody(templ) { + val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK))) + val supercall = New(supertpe, mmap(argss)(_.duplicate)) + val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall + ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck + atPos(supertpt.pos.focus)(supercall) + } match { + case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt) + case tpt => supertpt = TypeTree(tpt.tpe) setPos supertpt.pos.focus + } } + // this is the place where we tell the typer what argss should be used for the super call + // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`) + // the super call dummy is already good enough, so we don't need to do anything + if (argssAreTrivial) supertpt else supertpt updateAttachment SuperCallArgsAttachment(argss) + } + } - treeInfo.firstConstructor(templ.body) match { - case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => - // Convert constructor body to block in environment and typecheck it - val (preSuperStats, superCall) = { - val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) - (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) - } - val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall match { - case Apply(_, _) if supertparams.nonEmpty => transformSuperCall(superCall) - case _ => cunit.duplicate - }) - val outercontext = context.outer - - assert(clazz != NoSymbol, templ) - val cscope = outercontext.makeNewScope(constr, outercontext.owner) - val cbody2 = newTyper(cscope) // called both during completion AND typing. - .typePrimaryConstrBody(clazz, - cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) - - superCall match { - case Apply(_, _) => - val sarg = treeInfo.firstArgument(superCall) - if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - ConstrArgsInTraitParentTpeError(sarg, firstParent) - if (!supertparams.isEmpty) - supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) - } + /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template. + * Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`. + * `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit. + * + * ***Return value and side effects*** + * + * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked. + * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`. + * + * As a side effect, this method attributes the underlying fields of early vals. + * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody` + * at least once per definition. It'd be great to disentangle this logic at some point. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * the primary constructor of `templ` will be: + * + * Block(List( + * ValDef(NoMods, x, TypeTree(), 2) + * ValDef(NoMods, y, TypeTree(), 4) + * global.pendingSuperCall, + * Literal(Constant(()))) + * + * Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy, + * which encodes the fact that supercall argss are unknown during parsing and need to be transplanted + * from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. + */ + private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree = + treeInfo.firstConstructor(templ.body) match { + case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => + val (preSuperStats, superCall) = { + val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) + (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) + } + val superCall1 = (superCall match { + case global.pendingSuperCall => actualSuperCall + case EmptyTree => EmptyTree + }) orElse cunit + val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) + val clazz = context.owner + assert(clazz != NoSymbol, templ) + val cscope = context.outer.makeNewScope(ctor, context.outer.owner) + val cbody2 = { // called both during completion AND typing. + val typer1 = newTyper(cscope) + // XXX: see about using the class's symbol.... + clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym)) + typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate))) + typer1.typed(cbody1) + } - val preSuperVals = treeInfo.preSuperFields(templ.body) - if (preSuperVals.isEmpty && preSuperStats.nonEmpty) - devWarning("Wanted to zip empty presuper val list with " + preSuperStats) - else - map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) + val preSuperVals = treeInfo.preSuperFields(templ.body) + if (preSuperVals.isEmpty && preSuperStats.nonEmpty) + devWarning("Wanted to zip empty presuper val list with " + preSuperStats) + else + map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) - } + if (superCall1 == cunit) EmptyTree else cbody2 + case _ => + EmptyTree + } - // Certain parents are added in the parser before it is known whether - // that class also declared them as parents. For instance, this is an - // error unless we take corrective action here: - // - // case class Foo() extends Serializable - // - // So we strip the duplicates before typer. - def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match { - case Nil => Nil - case x :: xs => - val sym = x.symbol - x :: fixDuplicates( - if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) - else xs - ) + /** Makes sure that the first type tree in the list of parent types is always a class. + * If the first parent is a trait, prepend its supertype to the list until it's a class. + */ + private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match { + case first :: rest if treeInfo.isTraitRef(first) => + def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = { + if (treeInfo.isTraitRef(supertpt)) { + val supertpt1 = typedType(supertpt) + if (!supertpt1.isErrorTyped) { + val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + return explode(supersupertpt, supertpt1 :: acc) + } + } + if (supertpt.tpe.typeSymbol == AnyClass) supertpt.tpe = AnyRefClass.tpe + supertpt :: acc } + explode(first, Nil) ++ rest + case _ => parents + } - fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) - } - catch { - case ex: TypeError => - // fallback in case of cyclic errors - // @H none of the tests enter here but I couldn't rule it out - log("Type error calculating parents in template " + templ) - log("Error: " + ex) - ParentTypesError(templ, ex) - List(TypeTree(AnyRefClass.tpe)) - } + /** Certain parents are added in the parser before it is known whether + * that class also declared them as parents. For instance, this is an + * error unless we take corrective action here: + * + * case class Foo() extends Serializable + * + * So we strip the duplicates before typer. + */ + private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match { + case Nil => Nil + case x :: xs => + val sym = x.symbol + x :: fixDuplicateSyntheticParents( + if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) + else xs + ) + } + + def parentTypes(templ: Template): List[Tree] = templ.parents match { + case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) + case first :: rest => + try { + val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent( + typedParentType(first, templ, inMixinPosition = false) +: + (rest map (typedParentType(_, templ, inMixinPosition = true))))) + + // if that is required to infer the targs of a super call + // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck + // as a side effect, that typecheck also assigns types to the fields underlying early vals + // however if inference is not required, the typecheck doesn't happen + // and therefore early fields have their type trees not assigned + // here we detect this situation and take preventive measures + if (treeInfo.hasUntypedPreSuperFields(templ.body)) + typedPrimaryConstrBody(templ)(EmptyTree) + + supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) + } catch { + case ex: TypeError => + // fallback in case of cyclic errors + // @H none of the tests enter here but I couldn't rule it out + // upd. @E when a definitions inherits itself, we end up here + // because `typedParentType` triggers `initialize` for parent types symbols + log("Type error calculating parents in template " + templ) + log("Error: " + ex) + ParentTypesError(templ, ex) + List(TypeTree(AnyRefClass.tpe)) + } + } /** <p>Check that</p> * <ul> @@ -1757,9 +1873,12 @@ trait Typers extends Modes with Adaptations with Tags { // the following is necessary for templates generated later assert(clazz.info.decls != EmptyScope, clazz) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) - validateParentClasses(parents1, selfType) + if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore + validateParentClasses(parents1, selfType) if (clazz.isCase) validateNoCaseAncestor(clazz) + if (clazz.isTrait && hasSuperArgs(parents1.head)) + ConstrArgsInParentOfTraitError(parents1.head, clazz) if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) unit.error(clazz.pos, "inner classes cannot be classfile annotations") @@ -1767,9 +1886,21 @@ trait Typers extends Modes with Adaptations with Tags { if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) - val body = - if (isPastTyper || reporter.hasErrors) templ.body - else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val body = { + val body = + if (isPastTyper || reporter.hasErrors) templ.body + else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val primaryCtor = treeInfo.firstConstructor(body) + val primaryCtor1 = primaryCtor match { + case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) => + val argss = superCallArgs(parents1.head) getOrElse Nil + val pos = wrappingPos(parents1.head.pos, argss.flatten) + val superCall = atPos(pos)(PrimarySuperCall(argss)) + deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos + case _ => primaryCtor + } + body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } + } val body1 = typedStats(body, templ.symbol) @@ -2377,7 +2508,7 @@ trait Typers extends Modes with Adaptations with Tags { import CODE._ // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up - val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE_typed)).duplicate.asInstanceOf[CaseDef]) else Nil + val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE)).duplicate.asInstanceOf[CaseDef]) else Nil // println("casesTrue "+ casesTrue) def parentsPartial(targs: List[Type]) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, targs)) @@ -2463,7 +2594,7 @@ trait Typers extends Modes with Adaptations with Tags { methodSym setInfoAndEnter MethodType(paramSyms, BooleanClass.tpe) val match_ = methodBodyTyper.typedMatch(gen.mkUnchecked(selector), casesTrue, mode, BooleanClass.tpe) - val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(FALSE_typed), mode, BooleanClass.tpe) + val body = methodBodyTyper.virtualizedMatch(match_ updateAttachment DefaultOverrideMatchAttachment(FALSE), mode, BooleanClass.tpe) DefDef(methodSym, body) } @@ -2480,7 +2611,7 @@ trait Typers extends Modes with Adaptations with Tags { if (members.head eq EmptyTree) setError(tree) else { val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) } // Don't leak implementation details into the type, see SI-6575 if (isPartial && !typedBlock.isErrorTyped) @@ -2683,7 +2814,11 @@ trait Typers extends Modes with Adaptations with Tags { var moreToAdd = true while (moreToAdd) { val initElems = scope.elems - for (sym <- scope) + // SI-5877 The decls of a package include decls of the package object. But we don't want to add + // the corresponding synthetics to the package class, only to the package object class. + def shouldAdd(sym: Symbol) = + inBlock || !context.isInPackageObject(sym, context.owner) + for (sym <- scope if shouldAdd(sym)) for (tree <- context.unit.synthetics get sym) { newStats += typedStat(tree) // might add even more synthetics to the scope context.unit.synthetics -= sym @@ -3037,26 +3172,6 @@ trait Typers extends Modes with Adaptations with Tags { case _ => tp } - // Replace the Delegate-Chainer methods += and -= with corresponding - // + and - calls, which are translated in the code generator into - // Combine and Remove - if (forMSIL) { - fun match { - case Select(qual, name) => - if (isSubType(qual.tpe, DelegateClass.tpe) - && (name == encode("+=") || name == encode("-="))) { - val n = if (name == encode("+=")) nme.PLUS else nme.MINUS - val f = Select(qual, n) - // the compiler thinks, the PLUS method takes only one argument, - // but he thinks it's an instance method -> still two ref's on the stack - // -> translated by backend - val rhs = treeCopy.Apply(tree, f, args) - return typed(Assign(qual, rhs)) - } - case _ => () - } - } - /** * This is translating uses of List() into Nil. This is less * than ideal from a consistency standpoint, but it shouldn't be @@ -3728,7 +3843,7 @@ trait Typers extends Modes with Adaptations with Tags { case DynamicApplicationNamed(qual, _) if acceptsApplyDynamic(qual.tpe.widen) => true case _ => false // look deeper? - // val methPart = treeInfo.methPart(fun) + // val treeInfo.Applied(methPart, _, _) = fun // println("methPart of "+ fun +" is "+ methPart) // if (methPart ne fun) isApplyDynamicNamed(methPart) // else false @@ -3763,54 +3878,44 @@ trait Typers extends Modes with Adaptations with Tags { * */ def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = { - debuglog(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") + log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") + val treeInfo.Applied(treeSelection, _, _) = tree + def isDesugaredApply = treeSelection match { + case Select(`qual`, nme.apply) => true + case _ => false + } acceptsApplyDynamicWithType(qual, name) map { tp => - // tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user) - // in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct) - - // strip off type application -- we're not doing much with outer, - // so don't bother preserving cxTree's attributes etc - val cxTree1 = cxTree match { - case t: ValOrDefDef => t.rhs - case t => t - } - val cxTree2 = cxTree1 match { - case Typed(t, tpe) => t // ignore outer type annotation - case t => t - } - val (outer, explicitTargs) = cxTree2 match { - case TypeApply(fun, targs) => (fun, targs) - case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs) - case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs) - case t => (t, Nil) - } - def hasNamedArg(as: List[Tree]) = as.collectFirst{case AssignOrNamedArg(lhs, rhs) =>}.nonEmpty - - def desugaredApply = tree match { - case Select(`qual`, nme.apply) => true - case _ => false + // If tp == NoType, pass only explicit type arguments to applyXXX. Not used at all + // here - it is for scala-virtualized, where tp will be passed as an argument (for + // selection on a staged Struct) + def hasNamed(args: List[Tree]): Boolean = args exists (_.isInstanceOf[AssignOrNamedArg]) + // not supported: foo.bar(a1,..., an: _*) + def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args) + def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic + def matches(t: Tree) = isDesugaredApply || treeInfo.dissectApplied(t).core == treeSelection + + /** Note that the trees which arrive here are potentially some distance from + * the trees of direct interest. `cxTree` is some enclosing expression which + * may apparently be arbitrarily larger than `tree`; and `tree` itself is + * too small, having at least in some cases lost its explicit type parameters. + * This logic is designed to use `tree` to pinpoint the immediately surrounding + * Apply/TypeApply/Select node, and only then creates the dynamic call. + * See SI-6731 among others. + */ + def findSelection(t: Tree): Option[(TermName, Tree)] = t match { + case Apply(fn, args) if hasStar(args) => DynamicVarArgUnsupported(tree, applyOp(args)) ; None + case Apply(fn, args) if matches(fn) => Some((applyOp(args), fn)) + case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs)) + case _ if matches(t) => Some((nme.selectDynamic, t)) + case _ => t.children flatMap findSelection headOption } - // note: context.tree includes at most one Apply node - // thus, we can't use it to detect we're going to receive named args in expressions such as: - // qual.sel(a)(a2, arg2 = "a2") - val oper = outer match { - case Apply(q, as) if q == tree || desugaredApply => - val oper = - if (hasNamedArg(as)) nme.applyDynamicNamed - else nme.applyDynamic - // not supported: foo.bar(a1,..., an: _*) - if (treeInfo.isWildcardStarArgList(as)) { - DynamicVarArgUnsupported(tree, oper) - return Some(setError(tree)) - } else oper - case Assign(`tree`, _) => nme.updateDynamic - case _ => nme.selectDynamic + findSelection(cxTree) match { + case Some((opName, treeInfo.Applied(_, targs, _))) => + val fun = gen.mkTypeApply(Select(qual, opName), targs) + atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil)) + case _ => + setError(tree) } - - val dynSel = Select(qual, oper) - val tappSel = if (explicitTargs.nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel - - atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode))))) } } @@ -4001,8 +4106,8 @@ trait Typers extends Modes with Adaptations with Tags { return fail() if (treeInfo.mayBeVarGetter(varsym)) { - treeInfo.methPart(lhs1) match { - case Select(qual, name) => + lhs1 match { + case treeInfo.Applied(Select(qual, name), _, _) => val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos val app = Apply(sel, List(rhs)) setPos tree.pos return typed(app, mode, pt) @@ -4171,24 +4276,7 @@ trait Typers extends Modes with Adaptations with Tags { else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) case MethodType(formals, _) => if (isFunctionType(pt)) expr1 - else expr1 match { - case Select(qual, name) if (forMSIL && - pt != WildcardType && - pt != ErrorType && - isSubType(pt, DelegateClass.tpe)) => - val scalaCaller = newScalaCaller(pt) - addScalaCallerInfo(scalaCaller, expr1.symbol) - val n: Name = scalaCaller.name - val del = Ident(DelegateClass) setType DelegateClass.tpe - val f = Select(del, n) - //val f1 = TypeApply(f, List(Ident(pt.symbol) setType pt)) - val args: List[Tree] = if(expr1.symbol.isStatic) List(Literal(Constant(null))) - else List(qual) // where the scala-method is located - val rhs = Apply(f, args) - typed(rhs) - case _ => - adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) - } + else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) case ErrorType => expr1 case _ => @@ -4400,9 +4488,9 @@ trait Typers extends Modes with Adaptations with Tags { } case Apply(fn, indices) => - treeInfo.methPart(fn) match { - case Select(table, nme.apply) => mkUpdate(table, indices) - case _ => UnexpectedTreeAssignmentConversionError(qual) + fn match { + case treeInfo.Applied(Select(table, nme.apply), _, _) => mkUpdate(table, indices) + case _ => UnexpectedTreeAssignmentConversionError(qual) } } typed1(tree1, mode, pt) @@ -5015,7 +5103,7 @@ trait Typers extends Modes with Adaptations with Tags { case tt @ TypeTree() => tree setOriginal tt.original case _ => tree } - } + } else // we should get here only when something before failed // and we try again (@see tryTypedApply). In that case we can assign @@ -5235,21 +5323,21 @@ trait Typers extends Modes with Adaptations with Tags { // enough to see those. See #3938 ConstructorPrefixError(tree, restpe) } else { - //@M fix for #2208 - // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef - if (result.tpe.typeArgs.isEmpty) { - // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { - // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not - // designed to deal with the cycles in the scala package (ScalaObject extends - // AnyRef, but the AnyRef type alias is entered after the scala package is - // loaded and completed, so that ScalaObject is unpickled while AnyRef is not - // yet defined ) - // !!! TODO - revisit now that ScalaObject is gone. - result setType(restpe) - } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 - // during uncurry (after refchecks), all types are normalized - result - } + //@M fix for #2208 + // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef + if (result.tpe.typeArgs.isEmpty) { + // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { + // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not + // designed to deal with the cycles in the scala package (ScalaObject extends + // AnyRef, but the AnyRef type alias is entered after the scala package is + // loaded and completed, so that ScalaObject is unpickled while AnyRef is not + // yet defined ) + // !!! TODO - revisit now that ScalaObject is gone. + result setType(restpe) + } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 + // during uncurry (after refchecks), all types are normalized + result + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 061c6679da..3e76da4fdc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -114,7 +114,7 @@ trait Unapplies extends ast.TreeDSL ModuleDef( Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin), cdef.name.toTermName, - Template(parents, emptyValDef, NoMods, Nil, ListOfNil, body, cdef.impl.pos.focus)) + Template(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus)) } private val caseMods = Modifiers(SYNTHETIC | CASE) diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index ee66801c45..cbf6ef69d7 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -184,8 +184,7 @@ abstract class ClassPath[T] { def sourcepaths: IndexedSeq[AbstractFile] /** - * Represents classes which can be loaded with a ClassfileLoader/MsilFileLoader - * and / or a SourcefileLoader. + * Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader. */ case class ClassRep(binary: Option[T], source: Option[AbstractFile]) { def name: String = binary match { diff --git a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala b/src/compiler/scala/tools/nsc/util/MsilClassPath.scala deleted file mode 100644 index 2f209c550d..0000000000 --- a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala +++ /dev/null @@ -1,166 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2006-2013 LAMP/EPFL - * @author Martin Odersky - */ - -// $Id$ - -package scala.tools.nsc -package util - -import scala.util.Sorting -import scala.collection.mutable -import scala.tools.nsc.io.{ AbstractFile, MsilFile } -import ch.epfl.lamp.compiler.msil.{ Type => MSILType, Assembly } -import ClassPath.{ ClassPathContext, isTraitImplementation } - -/** Keeping the MSIL classpath code in its own file is important to make sure - * we don't accidentally introduce a dependency on msil.jar in the jvm. - */ - -object MsilClassPath { - def collectTypes(assemFile: AbstractFile) = { - var res: Array[MSILType] = MSILType.EmptyTypes - val assem = Assembly.LoadFrom(assemFile.path) - if (assem != null) { - // DeclaringType == null: true for non-inner classes - res = assem.GetTypes() filter (_.DeclaringType == null) - Sorting.stableSort(res, (t1: MSILType, t2: MSILType) => (t1.FullName compareTo t2.FullName) < 0) - } - res - } - - /** On the java side this logic is in PathResolver, but as I'm not really - * up to folding MSIL into that, I am encapsulating it here. - */ - def fromSettings(settings: Settings): MsilClassPath = { - val context = - if (settings.inline.value) new MsilContext - else new MsilContext { override def isValidName(name: String) = !isTraitImplementation(name) } - - import settings._ - new MsilClassPath(assemextdirs.value, assemrefs.value, sourcepath.value, context) - } - - class MsilContext extends ClassPathContext[MsilFile] { - def toBinaryName(rep: MsilFile) = rep.msilType.Name - def newClassPath(assemFile: AbstractFile) = new AssemblyClassPath(MsilClassPath collectTypes assemFile, "", this) - } - - private def assembleEntries(ext: String, user: String, source: String, context: MsilContext): List[ClassPath[MsilFile]] = { - import ClassPath._ - val etr = new mutable.ListBuffer[ClassPath[MsilFile]] - val names = new mutable.HashSet[String] - - // 1. Assemblies from -Xassem-extdirs - for (dirName <- expandPath(ext, expandStar = false)) { - val dir = AbstractFile.getDirectory(dirName) - if (dir ne null) { - for (file <- dir) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += context.newClassPath(file) - } - } - } - } - - // 2. Assemblies from -Xassem-path - for (fileName <- expandPath(user, expandStar = false)) { - val file = AbstractFile.getFile(fileName) - if (file ne null) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += context.newClassPath(file) - } - } - } - - def check(n: String) { - if (!names.contains(n)) - throw new AssertionError("Cannot find assembly "+ n + - ". Use -Xassem-extdirs or -Xassem-path to specify its location") - } - check("mscorlib.dll") - check("scalaruntime.dll") - - // 3. Source path - for (dirName <- expandPath(source, expandStar = false)) { - val file = AbstractFile.getDirectory(dirName) - if (file ne null) etr += new SourcePath[MsilFile](file, context) - } - - etr.toList - } -} -import MsilClassPath._ - -/** - * A assembly file (dll / exe) containing classes and namespaces - */ -class AssemblyClassPath(types: Array[MSILType], namespace: String, val context: MsilContext) extends ClassPath[MsilFile] { - def name = { - val i = namespace.lastIndexOf('.') - if (i < 0) namespace - else namespace drop (i + 1) - } - def asURLs = List(new java.net.URL(name)) - def asClasspathString = sys.error("Unknown") // I don't know what if anything makes sense here? - - private lazy val first: Int = { - var m = 0 - var n = types.length - 1 - while (m < n) { - val l = (m + n) / 2 - val res = types(l).FullName.compareTo(namespace) - if (res < 0) m = l + 1 - else n = l - } - if (types(m).FullName.startsWith(namespace)) m else types.length - } - - lazy val classes = { - val cls = new mutable.ListBuffer[ClassRep] - var i = first - while (i < types.length && types(i).Namespace.startsWith(namespace)) { - // CLRTypes used to exclude java.lang.Object and java.lang.String (no idea why..) - if (types(i).Namespace == namespace) - cls += ClassRep(Some(new MsilFile(types(i))), None) - i += 1 - } - cls.toIndexedSeq - } - - lazy val packages = { - val nsSet = new mutable.HashSet[String] - var i = first - while (i < types.length && types(i).Namespace.startsWith(namespace)) { - val subns = types(i).Namespace - if (subns.length > namespace.length) { - // example: namespace = "System", subns = "System.Reflection.Emit" - // => find second "." and "System.Reflection" to nsSet. - val end = subns.indexOf('.', namespace.length + 1) - nsSet += (if (end < 0) subns - else subns.substring(0, end)) - } - i += 1 - } - val xs = for (ns <- nsSet.toList) - yield new AssemblyClassPath(types, ns, context) - - xs.toIndexedSeq - } - - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() - - override def toString() = "assembly classpath "+ namespace -} - -/** - * The classpath when compiling with target:msil. Binary files are represented as - * MSILType values. - */ -class MsilClassPath(ext: String, user: String, source: String, context: MsilContext) -extends MergedClassPath[MsilFile](MsilClassPath.assembleEntries(ext, user, source, context), context) { } diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index a33af1754d..6997dbd402 100644 --- a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala +++ b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala @@ -6,7 +6,7 @@ package util import java.io.PrintStream /** A simple tracer - * @param out: The print stream where trace info shoul be sent + * @param out: The print stream where trace info should be sent * @param enabled: A condition that must be true for trace info to be produced. */ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index b1d343cee9..834f5436dc 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -148,13 +148,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => unwrapped = new Transformer { override def transform(tree: Tree): Tree = tree match { - case Ident(name) if invertedIndex contains name => + case Ident(name: TermName) if invertedIndex contains name => Ident(invertedIndex(name)) setType tree.tpe case _ => super.transform(tree) } }.transform(unwrapped) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name)))).traverse(unwrapped) + new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(unwrapped) unwrapped = if (expr0.isTerm) unwrapped else unwrapFromTerm(unwrapped) unwrapped } @@ -227,7 +227,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => emptyValDef, NoMods, List(), - List(List()), List(methdef), NoPosition)) trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala index 381dbd1d87..269cfa5dd4 100644 --- a/src/compiler/scala/tools/util/Javap.scala +++ b/src/compiler/scala/tools/util/Javap.scala @@ -6,12 +6,25 @@ package scala.tools package util +import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } import scala.tools.nsc.util.ScalaClassLoader -import java.io.{ InputStream, PrintWriter, ByteArrayInputStream } +import scala.tools.nsc.interpreter.IMain +import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, InputStream, + PrintWriter, Writer } +import java.util.{ Locale } +import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, + ForwardingJavaFileManager, JavaFileManager, JavaFileObject, + SimpleJavaFileObject, StandardLocation } import scala.tools.nsc.io.File -import Javap._ +import scala.io.Source +import scala.util.{ Try, Success, Failure } +import scala.util.Properties.lineSeparator +import scala.collection.JavaConverters +import scala.collection.generic.Clearable import scala.language.reflectiveCalls +import Javap._ + trait Javap { def loader: ScalaClassLoader def printWriter: PrintWriter @@ -29,137 +42,433 @@ object NoJavap extends Javap { } class JavapClass( - val loader: ScalaClassLoader = ScalaClassLoader.appLoader, - val printWriter: PrintWriter = new PrintWriter(System.out, true) + val loader: ScalaClassLoader, + val printWriter: PrintWriter, + intp: Option[IMain] = None ) extends Javap { + import JavapTool.ToolArgs - lazy val parser = new JpOptions - - val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull - val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull - private def failed = (EnvClass eq null) || (PrinterClass eq null) - - val PrinterCtr = ( - if (failed) null - else PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) - ) - - def findBytes(path: String): Array[Byte] = - tryFile(path) getOrElse tryClass(path) + lazy val tool = JavapTool() + /** Run the tool. Option args start with "-". + * The default options are "-protected -verbose". + * Byte data for filename args is retrieved with findBytes. + */ def apply(args: Seq[String]): List[JpResult] = { - if (failed) Nil - else args.toList filterNot (_ startsWith "-") map { path => - val bytes = findBytes(path) - if (bytes.isEmpty) new JpError("Could not find class bytes for '%s'".format(path)) - else new JpSuccess(newPrinter(new ByteArrayInputStream(bytes), newEnv(args))) - } + val (options, claases) = args partition (s => (s startsWith "-") && s.length > 1) + val (flags, upgraded) = upgrade(options) + if (flags.help || claases.isEmpty) List(JpResult(JavapTool.helper(printWriter))) + else tool(flags.raw, upgraded)(claases map (claas => claas -> bytesFor(claas))) } - def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = - if (failed) null - else PrinterCtr.newInstance(in, printWriter, env) - - def newEnv(opts: Seq[String]): FakeEnvironment = { - lazy val env: FakeEnvironment = EnvClass.newInstance() - - if (failed) null - else parser(opts) foreach { case (name, value) => - val field = EnvClass getDeclaredField name - field setAccessible true - field.set(env, value.asInstanceOf[AnyRef]) - } + /** Cull our tool options. */ + private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) = ToolArgs fromArgs options match { + case (t,s) if s.nonEmpty => (t,s) + case (t,s) => (t, JavapTool.DefaultOptions) + } - env + private def bytesFor(path: String) = Try { + def last = intp.get.mostRecentVar // fail if no intp + val bytes = findBytes(if (path == "-") last else path) + if (bytes.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '${path}'") + else bytes } + def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path) + /** Assume the string is a path and try to find the classfile * it represents. */ def tryFile(path: String): Option[Array[Byte]] = { - val file = File( + val file = if (path.endsWith(".class")) path else path.replace('.', '/') + ".class" - ) - if (!file.exists) None - else try Some(file.toByteArray) catch { case x: Exception => None } + (Try (File(file)) filter (_.exists) map (_.toByteArray)).toOption } /** Assume the string is a fully qualified class name and try to * find the class object it represents. */ def tryClass(path: String): Array[Byte] = { - val extName = ( - if (path endsWith ".class") (path dropRight 6).replace('/', '.') - else path + def pathology(p: String) = + if (p endsWith ".class") (p dropRight 6).replace('/', '.') + else p + def load(name: String) = loader classBytes pathology(name) + // if repl, translate the name to something replish + if (intp.isDefined) { + val claas = load(intp.get.translatePath(path) getOrElse path) + if (!claas.isEmpty) claas + // take path as a Name in scope and find its enclosing class + else intp.get.translateEnclosingClass(path) match { + case Some(encl) => load(encl) + case _ => claas // empty + } + } else load(path) + } + + abstract class JavapTool { + type ByteAry = Array[Byte] + type Input = Pair[String, Try[ByteAry]] + def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] + // Since the tool is loaded by reflection, check for catastrophic failure. + protected def failed: Boolean + implicit protected class Failer[A](a: =>A) { + def orFailed[B >: A](b: =>B) = if (failed) b else a + } + protected def noToolError = new JpError(s"No javap tool available: ${getClass.getName} failed to initialize.") + } + + class JavapTool6 extends JavapTool { + import JavapTool._ + val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull + val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull + override protected def failed = (EnvClass eq null) || (PrinterClass eq null) + + val PrinterCtr = PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) orFailed null + def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = + PrinterCtr.newInstance(in, printWriter, env) orFailed null + def showable(fp: FakePrinter) = new Showable { + def show() = fp.asInstanceOf[{ def print(): Unit }].print() + } + + lazy val parser = new JpOptions + def newEnv(opts: Seq[String]): FakeEnvironment = { + def result = { + val env: FakeEnvironment = EnvClass.newInstance() + parser(opts) foreach { case (name, value) => + val field = EnvClass getDeclaredField name + field setAccessible true + field.set(env, value.asInstanceOf[AnyRef]) + } + env + } + result orFailed null + } + + override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = + (inputs map { + case (_, Success(ba)) => JpResult(showable(newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) + case (_, Failure(e)) => JpResult(e.toString) + }).toList orFailed List(noToolError) + } + + class JavapTool7 extends JavapTool { + + import JavapTool._ + type Task = { + def call(): Boolean // true = ok + //def run(args: Array[String]): Int // all args + //def handleOptions(args: Array[String]): Unit // options, then run() or call() + } + // result of Task.run + //object TaskResult extends Enumeration { + // val Ok, Error, CmdErr, SysErr, Abnormal = Value + //} + val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull + override protected def failed = TaskClaas eq null + + val TaskCtor = TaskClaas.getConstructor( + classOf[Writer], + classOf[JavaFileManager], + classOf[DiagnosticListener[_]], + classOf[JIterable[String]], + classOf[JIterable[String]] + ) orFailed null + + class JavaReporter extends DiagnosticListener[JavaFileObject] with Clearable { + import scala.collection.mutable.{ ArrayBuffer, SynchronizedBuffer } + type D = Diagnostic[_ <: JavaFileObject] + val diagnostics = new ArrayBuffer[D] with SynchronizedBuffer[D] + override def report(d: Diagnostic[_ <: JavaFileObject]) { + diagnostics += d + } + override def clear() = diagnostics.clear() + /** All diagnostic messages. + * @param locale Locale for diagnostic messages, null by default. + */ + def messages(implicit locale: Locale = null) = (diagnostics map (_ getMessage locale)).toList + + def reportable(raw: Boolean): String = { + // don't filter this message if raw, since the names are likely to differ + val container = "Binary file .* contains .*".r + val m = if (raw) messages + else messages filter (_ match { case container() => false case _ => true }) + clear() + if (m.nonEmpty) m mkString ("", lineSeparator, lineSeparator) + else "" + } + } + val reporter = new JavaReporter + + // DisassemblerTool.getStandardFileManager(reporter,locale,charset) + val defaultFileManager: JavaFileManager = + (loader.tryToLoadClass[JavaFileManager]("com.sun.tools.javap.JavapFileManager").get getMethod ( + "create", + classOf[DiagnosticListener[_]], + classOf[PrintWriter] + ) invoke (null, reporter, new PrintWriter(System.err, true))).asInstanceOf[JavaFileManager] orFailed null + + // manages named arrays of bytes, which might have failed to load + class JavapFileManager(val managed: Seq[Input])(delegate: JavaFileManager = defaultFileManager) + extends ForwardingJavaFileManager[JavaFileManager](delegate) { + import JavaFileObject.Kind + import Kind._ + import StandardLocation._ + import JavaFileManager.Location + import java.net.URI + def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) + + def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 + def managedFile(name: String, kind: Kind) = kind match { + case CLASS => fileObjectForInput(name, inputNamed(name), kind) + case _ => null + } + // todo: just wrap it as scala abstractfile and adapt it uniformly + def fileObjectForInput(name: String, bytes: Try[ByteAry], kind: Kind): JavaFileObject = + new SimpleJavaFileObject(uri(name), kind) { + override def openInputStream(): InputStream = new ByteArrayInputStream(bytes.get) + // if non-null, ClassWriter wrongly requires scheme non-null + override def toUri: URI = null + override def getName: String = name + // suppress + override def getLastModified: Long = -1L + } + override def getJavaFileForInput(location: Location, className: String, kind: Kind): JavaFileObject = + location match { + case CLASS_PATH => managedFile(className, kind) + case _ => null + } + override def hasLocation(location: Location): Boolean = + location match { + case CLASS_PATH => true + case _ => false + } + } + val writer = new CharArrayWriter + def fileManager(inputs: Seq[Input]) = new JavapFileManager(inputs)() + def showable(raw: Boolean): Showable = { + val written = { + writer.flush() + val w = writer.toString + writer.reset() + w + } + val msgs = reporter.reportable(raw) + new Showable { + val mw = msgs + written + // ReplStrippingWriter clips and scrubs on write(String) + // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping + def show() = + if (raw && intp.isDefined) intp.get withoutUnwrapping { writeLines() } + else writeLines() + private def writeLines() { + for (line <- Source.fromString(mw).getLines) printWriter write line+lineSeparator + printWriter.flush() + } + } + } + // eventually, use the tool interface + def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { + //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). + //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) + import JavaConverters.asJavaIterableConverter + TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) + .orFailed (throw new IllegalStateException) + } + // a result per input + private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = + Try { + task(options, Seq(claas), inputs).call() + } map { + case true => JpResult(showable(raw)) + case _ => JpResult(reporter.reportable(raw)) + } recoverWith { + case e: java.lang.reflect.InvocationTargetException => e.getCause match { + case t: IllegalArgumentException => Success(JpResult(t.getMessage)) // bad option + case x => Failure(x) + } + } lastly { + reporter.clear + } + override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { + case (claas, Success(_)) => applyOne(raw, options, claas, inputs).get + case (_, Failure(e)) => JpResult(e.toString) + }).toList orFailed List(noToolError) + } + + object JavapTool { + // >= 1.7 + val Tool = "com.sun.tools.javap.JavapTask" + + // < 1.7 + val Env = "sun.tools.javap.JavapEnvironment" + val Printer = "sun.tools.javap.JavapPrinter" + // "documentation" + type FakeEnvironment = AnyRef + type FakePrinter = AnyRef + + // support JavapEnvironment + class JpOptions { + private object Access { + final val PRIVATE = 0 + final val PROTECTED = 1 + final val PACKAGE = 2 + final val PUBLIC = 3 + } + private val envActionMap: Map[String, (String, Any)] = { + val map = Map( + "-l" -> (("showLineAndLocal", true)), + "-c" -> (("showDisassembled", true)), + "-s" -> (("showInternalSigs", true)), + "-verbose" -> (("showVerbose", true)), + "-private" -> (("showAccess", Access.PRIVATE)), + "-package" -> (("showAccess", Access.PACKAGE)), + "-protected" -> (("showAccess", Access.PROTECTED)), + "-public" -> (("showAccess", Access.PUBLIC)), + "-all" -> (("showallAttr", true)) + ) + map ++ List( + "-v" -> map("-verbose"), + "-p" -> map("-private") + ) + } + def apply(opts: Seq[String]): Seq[(String, Any)] = { + opts flatMap { opt => + envActionMap get opt match { + case Some(pair) => List(pair) + case _ => + val charOpts = opt.tail.toSeq map ("-" + _) + if (charOpts forall (envActionMap contains _)) + charOpts map envActionMap + else Nil + } + } + } + } + + case class ToolArgs(raw: Boolean = false, help: Boolean = false) + + object ToolArgs { + def fromArgs(args: Seq[String]): (ToolArgs, Seq[String]) = ((ToolArgs(), Seq[String]()) /: (args flatMap massage)) { + case ((t,others), s) => s match { + case "-help" => (t copy (help=true), others) + case "-raw" => (t copy (raw=true), others) + case _ => (t, others :+ s) + } + } + } + + val helps = List( + "usage" -> ":javap [opts] [path or class or -]...", + "-help" -> "Prints this help message", + "-raw" -> "Don't unmangle REPL names", + "-verbose/-v" -> "Stack size, number of locals, method args", + "-private/-p" -> "Private classes and members", + "-package" -> "Package-private classes and members", + "-protected" -> "Protected classes and members", + "-public" -> "Public classes and members", + "-l" -> "Line and local variable tables", + "-c" -> "Disassembled code", + "-s" -> "Internal type signatures", + "-sysinfo" -> "System info of class", + "-constants" -> "Static final constants" ) - loader.classBytes(extName) + + // match prefixes and unpack opts, or -help on failure + def massage(arg: String): Seq[String] = { + require(arg startsWith "-") + // arg matches opt "-foo/-f" if prefix of -foo or exactly -f + val r = """(-[^/]*)(/(-.))?""".r + def maybe(opt: String, s: String): Option[String] = opt match { + // disambiguate by preferring short form + case r(lf,_,sf) if s == sf => Some(sf) + case r(lf,_,sf) if lf startsWith s => Some(lf) + case _ => None + } + def candidates(s: String) = (helps map (h => maybe(h._1, s))).flatten + // one candidate or one single-char candidate + def uniqueOf(maybes: Seq[String]) = { + def single(s: String) = s.length == 2 + if (maybes.length == 1) maybes + else if ((maybes count single) == 1) maybes filter single + else Nil + } + // each optchar must decode to exactly one option + def unpacked(s: String): Try[Seq[String]] = { + val ones = (s drop 1) map { c => + val maybes = uniqueOf(candidates(s"-$c")) + if (maybes.length == 1) Some(maybes.head) else None + } + Try(ones) filter (_ forall (_.isDefined)) map (_.flatten) + } + val res = uniqueOf(candidates(arg)) + if (res.nonEmpty) res + else (unpacked(arg) + getOrElse (Seq("-help"))) // or else someone needs help + } + + def helper(pw: PrintWriter) = new Showable { + def show() = helps foreach (p => pw write "%-12.12s%s%n".format(p._1,p._2)) + } + + val DefaultOptions = List("-protected", "-verbose") + + def isAvailable = Seq(Env, Tool) exists (cn => hasClass(loader, cn)) + + private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined + + private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) + + def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 } } +object JavapClass { + def apply( + loader: ScalaClassLoader = ScalaClassLoader.appLoader, + printWriter: PrintWriter = new PrintWriter(System.out, true), + intp: Option[IMain] = None + ) = new JavapClass(loader, printWriter, intp) +} + object Javap { - val Env = "sun.tools.javap.JavapEnvironment" - val Printer = "sun.tools.javap.JavapPrinter" - def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = - cl.tryToInitializeClass[AnyRef](Env).isDefined + def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable + + def apply(path: String): Unit = apply(Seq(path)) + def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) - // "documentation" - type FakeEnvironment = AnyRef - type FakePrinter = AnyRef + trait Showable { + def show(): Unit + } sealed trait JpResult { type ResultType def isError: Boolean def value: ResultType def show(): Unit + // todo + // def header(): String + // def fields(): List[String] + // def methods(): List[String] + // def signatures(): List[String] + } + object JpResult { + def apply(msg: String) = new JpError(msg) + def apply(res: Showable) = new JpSuccess(res) } class JpError(msg: String) extends JpResult { type ResultType = String def isError = true def value = msg - def show() = println(msg) + def show() = println(msg) // makes sense for :javap, less for -Ygen-javap } - class JpSuccess(val value: AnyRef) extends JpResult { + class JpSuccess(val value: Showable) extends JpResult { type ResultType = AnyRef def isError = false - def show() = value.asInstanceOf[{ def print(): Unit }].print() + def show() = value.show() // output to tool's PrintWriter } - - class JpOptions { - private object Access { - final val PRIVATE = 0 - final val PROTECTED = 1 - final val PACKAGE = 2 - final val PUBLIC = 3 - } - private val envActionMap: Map[String, (String, Any)] = { - val map = Map( - "-l" -> (("showLineAndLocal", true)), - "-c" -> (("showDisassembled", true)), - "-s" -> (("showInternalSigs", true)), - "-verbose" -> (("showVerbose", true)), - "-private" -> (("showAccess", Access.PRIVATE)), - "-package" -> (("showAccess", Access.PACKAGE)), - "-protected" -> (("showAccess", Access.PROTECTED)), - "-public" -> (("showAccess", Access.PUBLIC)), - "-all" -> (("showallAttr", true)) - ) - map ++ List( - "-v" -> map("-verbose"), - "-p" -> map("-private") - ) - } - def apply(opts: Seq[String]): Seq[(String, Any)] = { - opts flatMap { opt => - envActionMap get opt match { - case Some(pair) => List(pair) - case _ => - val charOpts = opt.tail.toSeq map ("-" + _) - if (charOpts forall (envActionMap contains _)) - charOpts map envActionMap - else Nil - } - } - } + implicit class Lastly[A](val t: Try[A]) extends AnyVal { + private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } + def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) } } |