Skip to content

Commit

Permalink
Macro def aware recompilation.
Browse files Browse the repository at this point in the history
 - Read macro modifier from method definition.
 - Always recompile downstream files after a file containing macro defs is recompiled.
 - Source is extended with a hasMacro attribute. Mark suggests that this might be better
   tracked in Relations, but I'm not sure how to make that change.
  • Loading branch information
retronym committed Mar 4, 2012
1 parent 69d45b7 commit 067917a
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 12 deletions.
1 change: 1 addition & 0 deletions api.specification
Expand Up @@ -234,6 +234,7 @@ Modifiers
isImplicit: Boolean isImplicit: Boolean
isLazy: Boolean isLazy: Boolean
isSynthetic: Boolean isSynthetic: Boolean
isMacro: Boolean
}}} }}}


{{{ {{{
Expand Down
23 changes: 21 additions & 2 deletions compile/api/APIUtil.scala
Expand Up @@ -9,11 +9,11 @@ object APIUtil
val modifiersToByte = (m: Modifiers) => { val modifiersToByte = (m: Modifiers) => {
import m._ import m._
def x(b: Boolean, bit: Int) = if(b) 1 << bit else 0 def x(b: Boolean, bit: Int) = if(b) 1 << bit else 0
( x(isAbstract, 0) | x(isOverride, 1) | x(isFinal, 2) | x(isSealed, 3) | x(isImplicit, 4) | x(isLazy, 5) ).toByte ( x(isAbstract, 0) | x(isOverride, 1) | x(isFinal, 2) | x(isSealed, 3) | x(isImplicit, 4) | x(isLazy, 5) | x(isMacro, 6) ).toByte
} }
val byteToModifiers = (b: Byte) => { val byteToModifiers = (b: Byte) => {
def x(bit: Int) = (b & (1 << bit)) != 0 def x(bit: Int) = (b & (1 << bit)) != 0
new Modifiers( x(0), x(1), x(2), x(3), x(4), x(5) ) new Modifiers( x(0), x(1), x(2), x(3), x(4), x(5), x(6))
} }


def verifyTypeParameters(s: SourceAPI): Boolean = def verifyTypeParameters(s: SourceAPI): Boolean =
Expand Down Expand Up @@ -43,6 +43,25 @@ object APIUtil
super.visitParameterRef(ref) super.visitParameterRef(ref)
} }
} }

def hasMacro(s: SourceAPI): Boolean =
{
val check = new HasMacro
check.visitAPI(s)
check.hasMacro
}

private[this] class HasMacro extends Visit
{
var hasMacro = false

override def visitModifiers(m: Modifiers)
{
hasMacro ||= m.isMacro
super.visitModifiers(m)
}
}

def minimize(api: SourceAPI): SourceAPI = def minimize(api: SourceAPI): SourceAPI =
new SourceAPI(api.packages, minimizeDefinitions(api.definitions)) new SourceAPI(api.packages, minimizeDefinitions(api.definitions))
def minimizeDefinitions(ds: Array[Definition]): Array[Definition] = def minimizeDefinitions(ds: Array[Definition]): Array[Definition] =
Expand Down
2 changes: 1 addition & 1 deletion compile/api/ClassToAPI.scala
Expand Up @@ -166,7 +166,7 @@ object ClassToAPI
def modifiers(i: Int): api.Modifiers = def modifiers(i: Int): api.Modifiers =
{ {
import Modifier.{isAbstract, isFinal} import Modifier.{isAbstract, isFinal}
new api.Modifiers( isAbstract(i), false, isFinal(i), false, false, false) new api.Modifiers( isAbstract(i), false, isFinal(i), false, false, false, false)
} }
def access(i: Int, pkg: Option[String]): api.Access = def access(i: Int, pkg: Option[String]): api.Access =
{ {
Expand Down
10 changes: 9 additions & 1 deletion compile/api/SameAPI.scala
Expand Up @@ -43,7 +43,14 @@ object TopLevel
/** Checks the API of two source files for equality.*/ /** Checks the API of two source files for equality.*/
object SameAPI object SameAPI
{ {
def apply(a: Source, b: Source): Boolean = a.apiHash == b.apiHash && (a.hash.length > 0 && b.hash.length > 0) && apply(a.api, b.api) def apply(a: Source, b: Source): Boolean =
{
// Never consider a file containing macros as having the same API as the previous run.
val hasMacro = a.hasMacro || b.hasMacro

!hasMacro && a.apiHash == b.apiHash && (a.hash.length > 0 && b.hash.length > 0) && apply(a.api, b.api)
}

def apply(a: SourceAPI, b: SourceAPI): Boolean = def apply(a: SourceAPI, b: SourceAPI): Boolean =
{ {
val start = System.currentTimeMillis val start = System.currentTimeMillis
Expand Down Expand Up @@ -208,6 +215,7 @@ class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, include
setIf(bs, isSealed, 3) setIf(bs, isSealed, 3)
setIf(bs, isImplicit, 4) setIf(bs, isImplicit, 4)
setIf(bs, isLazy, 5) setIf(bs, isLazy, 5)
setIf(bs, isMacro, 6)
bs.toImmutable bs.toImmutable
} }
def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit = def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit =
Expand Down
2 changes: 1 addition & 1 deletion compile/inc/APIs.scala
Expand Up @@ -38,7 +38,7 @@ object APIs


val emptyAPI = new xsbti.api.SourceAPI(Array(), Array()) val emptyAPI = new xsbti.api.SourceAPI(Array(), Array())
val emptyCompilation = new xsbti.api.Compilation(-1, "") val emptyCompilation = new xsbti.api.Compilation(-1, "")
val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0) val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0, false)
def getAPI[T](map: Map[T, Source], src: T): Source = map.getOrElse(src, emptySource) def getAPI[T](map: Map[T, Source], src: T): Source = map.getOrElse(src, emptySource)
} }


Expand Down
13 changes: 11 additions & 2 deletions compile/inc/Compile.scala
Expand Up @@ -52,6 +52,8 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
private[this] val sourceDeps = new HashMap[File, Set[File]] private[this] val sourceDeps = new HashMap[File, Set[File]]
private[this] val extSrcDeps = new ListBuffer[(File, String, Source)] private[this] val extSrcDeps = new ListBuffer[(File, String, Source)]
private[this] val binaryClassName = new HashMap[File, String] private[this] val binaryClassName = new HashMap[File, String]
// source files containing a macro def.
private[this] val macroSources = Set[File]()


private def add[A,B](map: Map[A,Set[B]], a: A, b: B): Unit = private def add[A,B](map: Map[A,Set[B]], a: A, b: B): Unit =
map.getOrElseUpdate(a, new HashSet[B]) += b map.getOrElseUpdate(a, new HashSet[B]) += b
Expand Down Expand Up @@ -99,7 +101,12 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
classToSource.put(module, source) classToSource.put(module, source)
} }


def api(sourceFile: File, source: SourceAPI) { apis(sourceFile) = (xsbt.api.HashAPI(source), xsbt.api.APIUtil.minimize(source)) } def api(sourceFile: File, source: SourceAPI) {
import xsbt.api.{APIUtil, HashAPI}
if (APIUtil.hasMacro(source)) macroSources += sourceFile
apis(sourceFile) = (HashAPI(source), APIUtil.minimize(source))
}

def endSource(sourcePath: File): Unit = def endSource(sourcePath: File): Unit =
assert(apis.contains(sourcePath)) assert(apis.contains(sourcePath))


Expand All @@ -110,7 +117,9 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
(base /: apis) { case (a, (src, api) ) => (base /: apis) { case (a, (src, api) ) =>
val stamp = current.internalSource(src) val stamp = current.internalSource(src)
val hash = stamp match { case h: Hash => h.value; case _ => new Array[Byte](0) } val hash = stamp match { case h: Hash => h.value; case _ => new Array[Byte](0) }
val s = new xsbti.api.Source(compilation, hash, api._2, api._1) // TODO store this in Relations, rather than Source.
val hasMacro: Boolean = macroSources.contains(src)
val s = new xsbti.api.Source(compilation, hash, api._2, api._1, hasMacro)
a.addSource(src, s, stamp, sourceDeps.getOrElse(src, Nil: Iterable[File])) a.addSource(src, s, stamp, sourceDeps.getOrElse(src, Nil: Iterable[File]))
} }
def addExternals(base: Analysis): Analysis = (base /: extSrcDeps) { case (a, (source, name, api)) => a.addExternalDep(source, name, api) } def addExternals(base: Analysis): Analysis = (base /: extSrcDeps) { case (a, (source, name, api)) => a.addExternalDep(source, name, api) }
Expand Down
4 changes: 2 additions & 2 deletions compile/interface/API.scala
Expand Up @@ -273,9 +273,9 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
} }
private def getModifiers(s: Symbol): xsbti.api.Modifiers = private def getModifiers(s: Symbol): xsbti.api.Modifiers =
{ {
import Flags._ import Flags._; val MACRO = 0x00008000 // From Flags.MACRO in 2.10.0+
new xsbti.api.Modifiers(s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED), s.hasFlag(OVERRIDE), new xsbti.api.Modifiers(s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED), s.hasFlag(OVERRIDE),
s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY)) s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), s.hasFlag(MACRO))
} }


private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT) private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT)
Expand Down
1 change: 1 addition & 0 deletions interface/other
Expand Up @@ -3,6 +3,7 @@ Source
hash: Byte* hash: Byte*
api: SourceAPI api: SourceAPI
apiHash: Int apiHash: Int
hasMacro: Boolean


SourceAPI SourceAPI
packages : Package* packages : Package*
Expand Down
12 changes: 9 additions & 3 deletions interface/src/main/java/xsbti/api/Modifiers.java
Expand Up @@ -8,21 +8,23 @@ public final class Modifiers implements java.io.Serializable
private static final int SealedBit = 3; private static final int SealedBit = 3;
private static final int ImplicitBit = 4; private static final int ImplicitBit = 4;
private static final int LazyBit = 5; private static final int LazyBit = 5;
private static final int MacroBit = 6;


private static final int flag(boolean set, int bit) private static final int flag(boolean set, int bit)
{ {
return set ? (1 << bit) : 0; return set ? (1 << bit) : 0;
} }


public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy) public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy, boolean isMacro)
{ {
this.flags = (byte)( this.flags = (byte)(
flag(isAbstract, AbstractBit) | flag(isAbstract, AbstractBit) |
flag(isOverride, OverrideBit) | flag(isOverride, OverrideBit) |
flag(isFinal, FinalBit) | flag(isFinal, FinalBit) |
flag(isSealed, SealedBit) | flag(isSealed, SealedBit) |
flag(isImplicit, ImplicitBit) | flag(isImplicit, ImplicitBit) |
flag(isLazy, LazyBit) flag(isLazy, LazyBit) |
flag(isMacro, MacroBit)
); );
} }


Expand Down Expand Up @@ -62,8 +64,12 @@ public final boolean isLazy()
{ {
return flag(LazyBit); return flag(LazyBit);
} }
public final boolean isMacro()
{
return flag(MacroBit);
}
public String toString() public String toString()
{ {
return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy()+ ")"; return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy() + ", " + "isMacro: " + isMacro()+ ")";
} }
} }

0 comments on commit 067917a

Please sign in to comment.