|
| 1 | +/* NSC -- new Scala compiler |
| 2 | + * Copyright 2005-2013 LAMP/EPFL |
| 3 | + */ |
| 4 | + |
| 5 | +package scala.reflect.macros |
| 6 | +package runtime |
| 7 | + |
| 8 | +import java.util.UUID._ |
| 9 | +import scala.reflect.internal.Flags._ |
| 10 | +import scala.reflect.internal.util.BatchSourceFile |
| 11 | +import scala.reflect.io.VirtualFile |
| 12 | + |
| 13 | +trait Synthetics { |
| 14 | + self: Context => |
| 15 | + |
| 16 | + import global._ |
| 17 | + import mirror.wrapMissing |
| 18 | + |
| 19 | + // getClassIfDefined and getModuleIfDefined cannot be used here |
| 20 | + // because they don't work for stuff declared in the empty package |
| 21 | + // (as specified in SLS, code inside non-empty packages cannot see |
| 22 | + // declarations from the empty package, so compiler internals |
| 23 | + // default to ignoring contents of the empty package) |
| 24 | + // to the contrast, staticModule and staticClass are designed |
| 25 | + // to be a part of the reflection API and, therefore, they |
| 26 | + // correctly resolve all names |
| 27 | + private def topLevelSymbol(name: Name): Symbol = wrapMissing { |
| 28 | + if (name.isTermName) mirror.staticModule(name.toString) |
| 29 | + else mirror.staticClass(name.toString) |
| 30 | + } |
| 31 | + |
| 32 | + def topLevelDef(name: Name): Tree = |
| 33 | + enclosingRun.units.toList.map(_.body).flatMap { |
| 34 | + // it's okay to check `stat.symbol` here, because currently macros expand strictly after namer |
| 35 | + // which means that by the earliest time one can call this method all top-level definitions will have already been entered |
| 36 | + case PackageDef(_, stats) => stats filter (stat => stat.symbol != NoSymbol && stat.symbol == topLevelSymbol(name)) |
| 37 | + case _ => Nil // should never happen, but better be safe than sorry |
| 38 | + }.headOption getOrElse EmptyTree |
| 39 | + |
| 40 | + def topLevelRef(name: Name): Tree = { |
| 41 | + if (topLevelDef(name).nonEmpty) gen.mkUnattributedRef(name) |
| 42 | + else EmptyTree |
| 43 | + } |
| 44 | + |
| 45 | + // TODO: provide a way to specify a pretty name for debugging purposes |
| 46 | + private def randomFileName() = ( |
| 47 | + "macroSynthetic-" + randomUUID().toString.replace("-", "") + ".scala" |
| 48 | + ) |
| 49 | + |
| 50 | + def introduceTopLevel[T: PackageSpec](packagePrototype: T, definition: universe.ImplDef): RefTree = |
| 51 | + introduceTopLevel(packagePrototype, List(definition)).head |
| 52 | + |
| 53 | + def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: universe.ImplDef*): List[RefTree] = |
| 54 | + introduceTopLevel(packagePrototype, definitions.toList) |
| 55 | + |
| 56 | + private def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: List[universe.ImplDef]): List[RefTree] = { |
| 57 | + val code @ PackageDef(pid, _) = implicitly[PackageSpec[T]].mkPackageDef(packagePrototype, definitions) |
| 58 | + val syntheticFileName = randomFileName() |
| 59 | + // compatibility with SBT |
| 60 | + // on the one hand, we need to specify some jfile here, otherwise sbt crashes with an NPE (SI-6870) |
| 61 | + // on the other hand, we can't specify the obvious enclosingUnit, because then sbt somehow fails to run tests using type macros |
| 62 | + // okay, now let's specify a guaranteedly non-existent file in an existing directory (so that we don't run into permission problems) |
| 63 | + val relatedJfile = enclosingUnit.source.file.file |
| 64 | + val fakeJfile = if (relatedJfile != null) new java.io.File(relatedJfile.getParent, syntheticFileName) else null |
| 65 | + val virtualFile = new VirtualFile(syntheticFileName) { override def file = fakeJfile } |
| 66 | + val sourceFile = new BatchSourceFile(virtualFile, code.toString) |
| 67 | + val unit = new CompilationUnit(sourceFile) |
| 68 | + unit.body = code |
| 69 | + universe.currentRun.compileLate(unit) |
| 70 | + definitions map (definition => Select(pid, definition.name)) |
| 71 | + } |
| 72 | + |
| 73 | + protected def mkPackageDef(name: String, stats: List[Tree]) = gen.mkPackageDef(name, stats) |
| 74 | + |
| 75 | + protected def mkPackageDef(name: TermName, stats: List[Tree]) = gen.mkPackageDef(name.toString, stats) |
| 76 | + |
| 77 | + protected def mkPackageDef(tree: RefTree, stats: List[Tree]) = PackageDef(tree, stats) |
| 78 | + |
| 79 | + protected def mkPackageDef(sym: Symbol, stats: List[Tree]) = { |
| 80 | + assert(sym hasFlag PACKAGE, s"expected a package or package class symbol, found: $sym") |
| 81 | + gen.mkPackageDef(sym.fullName.toString, stats) |
| 82 | + } |
| 83 | +} |
0 commit comments