blog-static/code/dyno-alloy/DynoAlloy.als

199 lines
6.8 KiB
Alloy

enum Flag {Method, MethodOrField, Public}
/* There is a negative version for each flag (METHOD and NOT_METHOD).
Model this as two sets, one of positive flags, and one of netative flags,
and interpret the bitfield to be a conjunction of both flags. */
sig Bitfield {
, positiveFlags: set Flag
, negativeFlags: set Flag
}
/* A filter state has filterFlags and excludeFlags, both represented as conjunctions. */
sig FilterState {
, include: Bitfield
, exclude: Bitfield
}
/* Initially, no search has happeneed for a scope, so its 'found' is not set to anything. */
one sig NotSet {}
/* Finally, there's a search state (whether or not a particular scope has already been
searched with a particular configuration). */
one sig SearchState {
, var found: Bitfield + NotSet
}
pred bitfieldEmpty[b: Bitfield] {
#b.positiveFlags = 0 and #b.negativeFlags = 0
}
pred bitfieldEqual[b1: Bitfield, b2: Bitfield] {
b1.positiveFlags = b2.positiveFlags and b1.negativeFlags = b2.negativeFlags
}
pred bitfieldIntersection[b1: Bitfield, b2: Bitfield, b3: Bitfield] {
b3.positiveFlags = b1.positiveFlags & b2.positiveFlags
b3.negativeFlags = b1.negativeFlags & b2.negativeFlags
}
pred bitfieldSubset[b1: Bitfield, b2: Bitfield] {
b1.positiveFlags in b2.positiveFlags
b1.negativeFlags in b2.negativeFlags
}
pred bitfieldIncomparable[b1: Bitfield, b2: Bitfield] {
not bitfieldSubset[b1, b2]
not bitfieldSubset[b2, b1]
}
pred addBitfieldFlag[b1: Bitfield, b2: Bitfield, flag: Flag] {
b2.positiveFlags = b1.positiveFlags + flag
b2.negativeFlags = b1.negativeFlags
}
pred addBitfieldFlagNeg[b1: Bitfield, b2: Bitfield, flag: Flag] {
b2.negativeFlags = b1.negativeFlags + flag
b2.positiveFlags = b1.positiveFlags
}
enum Property { PMethod, PField, PPublic }
sig Symbol {
properties: set Property
}
pred flagMatchesPropery[flag: Flag, property: Property] {
(flag = Method and property = PMethod) or
(flag = MethodOrField and (property = PMethod or property = PField)) or
(flag = Public and property = PPublic)
}
pred bitfieldMatchesProperties[bitfield: Bitfield, symbol: Symbol] {
all flag: bitfield.positiveFlags | some property: symbol.properties | flagMatchesPropery[flag, property]
all flag: bitfield.negativeFlags | no property: symbol.properties | flagMatchesPropery[flag, property]
}
bitfieldExists: run {
some Bitfield
}
matchingBitfieldExists: run {
some bitfield : Bitfield, symbol : Symbol | bitfieldMatchesProperties[bitfield, symbol]
}
matchingBitfieldExists2: run {
some bitfield : Bitfield, symbol : Symbol {
#bitfield.positiveFlags = 1
#bitfield.negativeFlags = 1
#symbol.properties = 2
bitfieldMatchesProperties[bitfield, symbol]
}
}
fact "method and field are incompatible" {
always no symbol: Symbol | {
PMethod in symbol.properties and PField in symbol.properties
}
}
fact "public and field are incompatible" {
always no symbol: Symbol | {
PPublic in symbol.properties and PField in symbol.properties
}
}
matchingBitfieldExists3: run {
some bitfield : Bitfield, symbol : Symbol {
#bitfield.positiveFlags = 2
#symbol.properties = 2
bitfieldMatchesProperties[bitfield, symbol]
}
}
pred configureState[filterState: FilterState] {
some initialState: FilterState {
// Each lookup in scope starts with empty filter and exclude flags
bitfieldEmpty[initialState.include] and bitfieldEmpty[initialState.exclude]
// The intermediate states (bf1) are used for sequencing of operations.
some bf1 : Bitfield {
// Add "Public" depending on skipPrivateVisibilities
addBitfieldFlag[initialState.include, bf1, Public] or
bitfieldEqual[initialState.include, bf1]
// If it's a method receiver, add method or field restriction
addBitfieldFlag[bf1, filterState.include, MethodOrField] or
// if it's not a receiver, filter to non-methods (could be overridden)
addBitfieldFlagNeg[bf1, filterState.include, Method] or
// Maybe methods are not being included but it's not a receiver, so no change.
bitfieldEqual[bf1, filterState.include]
}
// Exclude filter doesn't change here
initialState.exclude = filterState.exclude
}
}
pred oldUpdate[toSet: Bitfield + NotSet, setTo: FilterState] {
toSet' in Bitfield and bitfieldIntersection[toSet, setTo.include, toSet']
}
pred newUpdate[toSet: Bitfield + NotSet, setTo: FilterState] {
(not bitfieldIncomparable[toSet, setTo.include] and oldUpdate[toSet, setTo]) or
(bitfieldIncomparable[toSet, setTo.include] and toSet = toSet')
}
pred updateOrSet[toSet: Bitfield + NotSet, setTo: FilterState] {
(toSet in NotSet and toSet' = setTo.include) or
(toSet not in NotSet and oldUpdate[toSet, setTo])
}
pred excludeBitfield[found: Bitfield + NotSet, exclude: Bitfield] {
(found != NotSet and bitfieldEqual[found, exclude]) or
(found = NotSet and bitfieldEmpty[exclude])
}
fact init {
all searchState: SearchState | searchState.found = NotSet
}
fact step {
always {
// Model that a new doLookupInScope could've occurred, with any combination of flags.
all searchState: SearchState {
some fs: FilterState {
// This is a possible combination of lookup flags
configureState[fs]
// If a search has been performed before, take the intersection; otherwise,
// just insert the current filter flags.
updateOrSet[searchState.found, fs]
}
}
}
}
example: run {
all searchState: SearchState {
// a way that subsequent results of searching it will miss things.
eventually some symbol: Symbol, fs: FilterState, fsBroken: FilterState, exclude: Bitfield {
// Some search (fs) will cause a transition / modification of the search state...
configureState[fs]
updateOrSet[searchState.found, fs]
// Such that a later, valid search... (fsBroken)
configureState[fsBroken]
excludeBitfield[searchState.found', exclude]
// Will allow for a symbol ...
// ... that are left out of the original search...
not bitfieldMatchesProperties[searchState.found, symbol]
// ... and out of the current search
not (bitfieldMatchesProperties[fs.include, symbol] and not bitfieldMatchesProperties[searchState.found, symbol])
// But would be matched by the broken search...
bitfieldMatchesProperties[fsBroken.include, symbol]
// ... to not be matched by a search with the new state:
not (bitfieldMatchesProperties[fsBroken.include, symbol] and not bitfieldMatchesProperties[exclude, symbol])
}
}
}