Skip to content

Latest commit

 

History

History
552 lines (459 loc) · 28.9 KB

File metadata and controls

552 lines (459 loc) · 28.9 KB

Merge Plan: DSL + FAIR + FairDSL.kt → DSL-FAIR

1. Goal

Produce a single Kotlin project in DSL-FAIR/ that:

  • Uses FairDSL.kt's design (expression-level handlers, matcher-based dispatch, DomainOperation callbacks, two-phase architecture) as the DSL surface.
  • Runs on real SootUp Jimple IR (not the mock JimpleExpr sealed class from FairDSL.kt).
  • Reuses FAIR's converter infrastructure (AIRMethodConverter, AirTranslationSuite, air.core) for walking Jimple CFGs and producing AIR/FAIR output.
  • Incorporates the richer lattice library from DSL (ProductLattice, MapLattice, LiftLattice) into FairDSL.kt's Lattice<T> interface.

2. Architecture Overview

┌──────────────────────────────────────────────────────────────────────┐
│  User-facing DSL (from FairDSL.kt design)                           │
│                                                                      │
│  fairAnalysis("Taint", TaintLattice) {                              │
│      onInvokeExpr { source(...) { taintsReturnValue() } }           │
│      // binop, unary, alloc defaults just work                      │
│  }                                                                   │
│         │                                                            │
│         ▼  builds                                                    │
│  FairAnalysisDefinition<T>                                          │
│    .processExpression(expr: Expr) → List<FairApply<T>>              │
└──────────┬───────────────────────────────────────────────────────────┘
           │ called by
┌──────────▼───────────────────────────────────────────────────────────┐
│  FAIR Converter (from FAIR, adapted)                                 │
│                                                                      │
│  AIRMethodConverter walks Jimple CFG (Pass 1 + Pass 2)              │
│  DefaultAIRStmtSemantics.translateAssign() sees RHS expression:     │
│    → calls analysisDefinition.processExpression(rhs)                │
│    → if non-empty: emits AIRApplyStmt per FairApply                 │
│    → if empty: falls through to structural default (copy/load/store)│
└──────────┬───────────────────────────────────────────────────────────┘
           │ produces
┌──────────▼───────────────────────────────────────────────────────────┐
│  AIR Instructions (from air.core, kept as-is)                        │
│                                                                      │
│  AIRApplyStmt now carries a DomainOperation<T> reference             │
│  (or an OpId that maps to one) so the interpreter can call back     │
│  into the DSL's flow function at execution time.                    │
└──────────────────────────────────────────────────────────────────────┘

3. Project Structure

DSL-FAIR/
├── build.gradle.kts              — single project (no submodules initially)
├── settings.gradle.kts
└── src/main/kotlin/
    ├── dsl/                      — DSL surface (from FairDSL.kt, adapted)
    │   ├── Lattice.kt            — merged lattice interface + implementations
    │   ├── DomainOperation.kt    — DomainOperation<T> + five generic ops
    │   ├── ExprHandlers.kt       — ExprHandler hierarchy (uses real SootUp Expr types)
    │   ├── InvokeHandlers.kt     — InvokeExprHandler + source/sink/sanitizer builders
    │   ├── FairAnalysis.kt       — FairAnalysisBuilder, FairAnalysisDefinition, fairAnalysis()
    │   ├── Environment.kt        — AbstractEnvironment<T>, Finding, FindingReporter
    │   └── examples/
    │       └── TaintAnalysis.kt  — defineTaintAnalysis() example
    ├── air/                      — copied from FAIR/air.core (kept as-is, mostly)
    │   ├── AIR.kt
    │   ├── AIRStmt.kt
    │   ├── AIRAccessPath.kt
    │   ├── AIRValue.kt
    │   ├── ... (all air.core files)
    │   └── model/
    │       └── ...
    ├── converter/                — copied from FAIR/src, adapted
    │   ├── AIRMethodConverter.kt — unchanged (delegates to semantics)
    │   ├── AIRWholeProgramConverter.kt
    │   ├── AirTranslationSuite.kt — extended to accept FairAnalysisDefinition
    │   ├── BinaryOpFinder.kt
    │   ├── LiftValueHelper.kt
    │   └── ...
    ├── semantics/                — from FAIR/src, with DSL integration point
    │   ├── AIRSemantics.kt
    │   ├── AIRSemanticsContext.kt — gains reference to FairAnalysisDefinition
    │   ├── DslDrivenSemantics.kt — NEW: replaces TaintIFDSSemantics
    │   ├── expr/
    │   │   └── DslExprSemantics.kt — NEW: delegates to DSL handlers
    │   ├── stmt/
    │   │   └── DslStmtSemantics.kt — NEW: calls DSL for expression RHS
    │   ├── call/
    │   │   └── DslCallSemantics.kt — NEW: calls DSL invoke handler
    │   ├── heap/
    │   │   └── DefaultHeapSemantics.kt — kept
    │   └── wholeProgram/
    │       └── WholeProgramContext.kt — kept
    ├── sensitivity/              — copied from FAIR/src as-is
    │   ├── AccessPathFactory.kt
    │   └── SensitivityType.kt
    ├── helper/                   — copied from FAIR/src as-is
    │   ├── CallGraphIndex.kt
    │   └── FoldHelper.kt
    └── rule/                     — from FAIR/src, kept for backward compat
        ├── MethodRule.kt
        └── TaintMethodRule.kt    — kept but optional (DSL replaces it)

4. What to Keep (Copy As-Is)

From FAIR/air.core/ — ALL files

The AIR instruction set is the target IR. Copy the entire air.core source tree. No changes needed, except one small extension to AIRApplyStmt (see Section 6).

Files:

  • AIR.kt, AIRStmt.kt, AIRAccessPath.kt, AIRValue.kt, AIRLocal.kt, AIRField.kt
  • AIRConstant.kt, AIROperator.kt, AIRBody.kt, ComparisonOp.kt, CallType.kt
  • model/ (AIRCFG, AirBasicBlock, AIRMethod, AIRClass, AIRProgram, signatures, types, lattice, modifiers, ForEachSet, etc.)
  • context/ (ContextId, ContextStatementFactory, CallStringsContextFactory, IFDSContextFactory, NoContextFactory, MethodId, MethodIdRegistry, ContextToken, ContextInitSource)

From FAIR/src/ — infrastructure files (copy as-is)

  • converter/AIRMethodConverter.kt — the two-pass CFG walker. No changes; it already delegates all expression/statement logic to AIRSemantics.
  • converter/AIRWholeProgramConverter.kt — whole-program entry point.
  • converter/AIRClassConverter.kt — class-level conversion.
  • converter/BinaryOpFinder.kt — maps SootUp binop to AIR BinaryOp.
  • converter/LiftValueHelper.kt — lifts SootUp Values to AIRValues.
  • converter/ClassTypeFactory.kt, TypeExtension.kt — type mapping utilities.
  • helper/CallGraphIndex.kt — call graph indexing.
  • helper/FoldHelper.kt, PlaceHolderFieldSignature.kt — fold helpers.
  • sensitivity/AccessPathFactory.kt, SensitivityType.kt, AIRLocalFactory.kt — access path creation.
  • semantics/AIRSemantics.kt — the composed interface (kept as-is).
  • semantics/AIRSemanticsContext.kt — extended (see Section 6).
  • semantics/FrameworkType.kt — enum kept.
  • semantics/stmt/AIRStmtSemantics.kt — interface kept.
  • semantics/stmt/DefaultAIRStmtSemantics.kt — kept, extended (see Section 6).
  • semantics/stmt/AnalysisDirectionSemantics.kt, ForwardDirectionSemantics.kt, BackwardDirectionSemantics.kt — kept.
  • semantics/expr/AIRExprSemantics.kt — interface kept.
  • semantics/call/AIRCallSemantics.kt, AbstractCallSemantics.kt, CallHelper.kt — kept.
  • semantics/call/IFDSCallSemantics.kt, MonotoneCallSemantics.kt — kept for non-DSL paths.
  • semantics/heap/AIRHeapSemantics.kt, DefaultHeapSemantics.kt — kept.
  • semantics/wholeProgram/WholeProgramContext.kt, TaintProgramContext.kt — kept.
  • rule/MethodRule.kt, MethodRulePattern.kt, DefaultJVMRules.kt, StringConcatRule.kt — kept for backward compatibility.
  • rule/TaintMethodRule.kt — kept but the DSL replaces its role.
  • ConversionUtil.kt, SootUpManager.kt, AnalysisType.kt, ContextStrategy.kt, AnalysisDirection.kt, ProgramConfig.kt — kept.
  • Main.kt — adapted for DSL entry point.

From DSL/src/ — lattice implementations only

  • Lattice.kt — the FlatLattice<E>, MapLattice<K,V>, LiftLattice<E>, ProductLattice<A,B> implementations are richer than FairDSL.kt's. We merge them into the FairDSL.kt Lattice<T> interface (see Section 6).

From DSL/src/ — NOT kept

  • Models.kt — statement-level Pattern/Effect model. Superseded by FairDSL.kt's expression-level handlers.
  • AnalysisBuilder.kt — top-level DSL builder. Superseded by FairAnalysisBuilder<T>.
  • RulesBuilder.kt, Rules.ktyields infix operator and pattern helpers. Superseded by handler-based dispatch.
  • Globals.kt — SOURCE/SINK/SANITIZE singletons. Replaced by matcher-based source/sink/sanitizer builders.
  • printAnalysis.kt — pretty-printer for the old model. Will need rewriting for the new model.
  • Main.kt — example using the old DSL. Replaced by TaintAnalysis.kt example.

From FairDSL.kt — design kept, implementation adapted

The entire file is the design blueprint. We keep:

  • Lattice<T> interface (with meet and leq, which DSL's version lacks)
  • DomainOperation<T> fun interface + five generic ops
  • ExprHandler<T> hierarchy
  • InvokeExprHandler<T> with source/sink/sanitizer builders
  • FairAnalysisBuilder<T>, FairAnalysisDefinition<T>, fairAnalysis() entry point
  • AbstractEnvironment<T>, Finding, FindingReporter

But we replace the mock JimpleExpr sealed class and MethodSignature with real SootUp types.

5. What NOT to Keep

File/Concept Reason
DSL/src/Models.kt (Pattern, Effect, FlowRule) Statement-level model; FairDSL.kt uses expression-level handlers
DSL/src/AnalysisBuilder.kt Replaced by FairAnalysisBuilder<T>
DSL/src/RulesBuilder.kt, Rules.kt yields pattern superseded by handler dispatch
DSL/src/Globals.kt (SOURCE, SINK, SANITIZE) Replaced by matcher-based source(), sink(), sanitizer() builders
DSL/src/printAnalysis.kt Tied to old model
DSL/src/Main.kt Old example
FairDSL.kt §10 (JimpleExpr sealed class) Mock types; replaced by real SootUp Expr subclasses
FairDSL.kt §10 (MethodSignature data class) Mock; replaced by SootUp's sootup.core.signatures.MethodSignature
FAIR/.../TaintIFDSSemantics.kt Hardcoded taint semantics; replaced by DslDrivenSemantics
FAIR/.../AnalysisPolicyProvider.kt Hardcoded policy; replaced by DSL-driven provider
FAIR/.../IFDSExprSemantics.kt Hardcoded IFDS expr handling; replaced by DslExprSemantics
FAIR/.../IFDSAIRStmtSemantics.kt IFDS-specific stmt handling; replaced by DslStmtSemantics

6. What Needs to Change (Extensions / Adaptations)

6.1 Lattice Interface Merge

Problem: FairDSL.kt's Lattice<T> has {bottom, top, join, meet, leq}. DSL's Lattice<E> has {elements, bottom, top, join} but no meet or leq. The DSL has richer implementations (MapLattice, LiftLattice, ProductLattice) that FairDSL.kt lacks.

Solution: Use FairDSL.kt's interface as the canonical one and port DSL's implementations to it:

// dsl/Lattice.kt — merged interface
interface Lattice<T> {
    val bottom: T
    val top: T
    fun join(a: T, b: T): T
    fun meet(a: T, b: T): T
    fun leq(a: T, b: T): Boolean
}

Port from DSL:

  • FlatLattice<E> — add meet and leq (straightforward duals of join).
  • MapLattice<K,V> — add meet (pointwise meet) and leq (pointwise leq).
  • LiftLattice<E> — add meet and leq.
  • ProductLattice<A,B> — add meet (component-wise) and leq (component-wise).

Drop elements: Set<E> from the interface (it's impractical for MapLattice etc. — FairDSL.kt correctly omits it).

Keep FairDSL.kt's Flat<E> sealed class (Bottom | Top | Element<E>) and FlatLattice<E> as a separate utility alongside the ported DSL lattices.

6.2 Expression Handlers: Mock Types → Real SootUp Types

Problem: FairDSL.kt's ExprHandler.process() takes JimpleExpr (a mock sealed class). The real SootUp types are sootup.core.jimple.common.expr.Expr and its subclasses.

Solution: Replace JimpleExpr parameter with SootUp Expr (or Value in some cases). The handler hierarchy becomes:

abstract class ExprHandler<T>(
    protected val lattice: Lattice<T>,
    protected var defaultOp: DomainOperation<T>
) {
    // Now takes SootUp Expr + the AIR destination access path + context
    abstract fun process(
        dest: AIRAccessPath?,
        expr: Expr,          // real SootUp type
        context: AIRSemanticsContext
    ): List<AIRApplyStmt>    // returns AIR statements, not FairApply
}

Key change: Instead of returning FairApply<T> (a lightweight data class), handlers now return AIRApplyStmt — the real AIR instruction. The DomainOperation<T> is embedded via a new AIROperator wrapper (see 6.3).

Handler-to-SootUp mapping:

  • BinaryExprHandler<T> processes AbstractBinopExpr
  • UnaryExprHandler<T> / NegExprHandler<T> processes JNegExpr
  • LengthExprHandler<T> processes JLengthExpr
  • CastExprHandler<T> processes JCastExpr
  • InstanceOfExprHandler<T> processes JInstanceOfExpr
  • InvokeExprHandler<T> processes AbstractInvokeExpr
  • AllocationExprHandler<T> processes JNewExpr, JNewArrayExpr, JNewMultiArrayExpr

Each handler uses LiftValueHelper.liftValue() and LiftValueHelper.liftAccessPath() to convert SootUp values to AIR values/access paths.

6.3 AIRApplyStmt: Carrying DomainOperation

Problem: AIR.AIRApplyStmt currently takes AIROperator (an interface with implementations like BinaryOp, TaintOperator). FairDSL.kt's FairApply<T> carries a DomainOperation<T> that the interpreter calls at execution time. We need AIRApplyStmt to also carry a DomainOperation.

Solution: Create a DslOperator<T> that implements AIROperator and wraps a DomainOperation<T>:

// New class bridging DSL operations to AIR's operator model
class DslOperator<T>(
    val operation: DomainOperation<T>,
    private val label: String = operation.toString()
) : AIROperator {
    override fun toString(): String = label
}

AIRApplyStmt already accepts any AIROperator, so no change to its data class. The interpreter, when it encounters an AIRApplyStmt whose operator is a DslOperator, calls operation.execute(dest, args, env).

6.4 InvokeExprHandler: Real SootUp MethodSignature Matching

Problem: FairDSL.kt's InvokeExprMatcher takes the mock MethodSignature. We need it to match on SootUp's real sootup.core.signatures.MethodSignature.

Solution:

fun interface InvokeExprMatcher {
    fun matches(signature: sootup.core.signatures.MethodSignature): Boolean
}

// Convenience helpers
fun matching(predicate: (sootup.core.signatures.MethodSignature) -> Boolean) =
    InvokeExprMatcher(predicate)

// Named matchers for common cases
fun methodNamed(name: String) = matching { it.subSignature.name == name }
fun methodIn(className: String) = matching { it.declClassType.fullyQualifiedName == className }

6.5 DslStmtSemantics: The Core Integration Point

Problem: DefaultAIRStmtSemantics.translateAssign() currently dispatches the RHS of an assignment to specific semantics (expr, call, heap). We need it to first consult the DSL for expression-level rules, and only fall through to structural defaults if the DSL returns nothing.

Solution: Create DslStmtSemantics extending DefaultAIRStmtSemantics:

class DslStmtSemantics<T>(
    private val analysisDefinition: FairAnalysisDefinition<T>,
    exprSemantics: AIRExprSemantics,
    heapSemantics: AIRHeapSemantics,
    callSemantics: AIRCallSemantics,
) : DefaultAIRStmtSemantics(exprSemantics, heapSemantics, callSemantics) {

    override fun translateAssign(context: AIRSemanticsContext, stmt: JAssignStmt): List<AIRStmt> {
        val rhs = stmt.rightOp

        // Phase 1 callback: ask DSL for expression-level handling
        if (rhs is Expr) {
            val dest = liftAccessPath(context.sensitivity, stmt.leftOp, context)
            val dslResult = analysisDefinition.processExpression(dest, rhs, context)
            if (dslResult.isNotEmpty()) return dslResult
        }

        // Fall through to structural defaults (copy, load, store, constant)
        return super.translateAssign(context, stmt)
    }
}

This is the key integration point: the converter's existing two-pass CFG walk calls translateAssign, which now calls back into the DSL for expression handling. If the DSL has a rule (e.g., source/sink/sanitizer for an invoke, or a custom binop handler), it returns AIRApplyStmts. Otherwise, the existing structural translation (copy, load, store, constant) kicks in unchanged.

6.6 DslCallSemantics: Invoke Handling

Problem: For JInvokeStmt (void calls, not in an assignment), the DSL's invoke handler also needs to be consulted — e.g., a sink call sqlQuery(x) that has no LHS.

Solution: Create DslCallSemantics<T> wrapping AbstractCallSemantics:

class DslCallSemantics<T>(
    private val analysisDefinition: FairAnalysisDefinition<T>,
    private val delegate: AbstractCallSemantics
) : AIRCallSemantics {

    override fun translateInvokeExpr(
        destination: AIRAccessPath?,
        context: AIRSemanticsContext,
        invokeStmt: Stmt,
    ): List<AIRStmt> {
        // Extract the invoke expression
        val invokeExpr = extractInvokeExpr(invokeStmt)

        // Ask DSL invoke handler first
        val dslResult = analysisDefinition.processInvoke(destination, invokeExpr, context)
        if (dslResult.isNotEmpty()) return dslResult

        // Fall through to standard call handling (call stmt generation, virtual dispatch)
        return delegate.translateInvokeExpr(destination, context, invokeStmt)
    }
}

6.7 FairAnalysisDefinition: Adapted for Real SootUp Types

The processExpression method dispatches on real SootUp expression types:

class FairAnalysisDefinition<T>(
    val name: String,
    val lattice: Lattice<T>,
    private val invokeHandler: InvokeExprHandler<T>,
    private val binopHandler: BinaryExprHandler<T>,
    // ... all handlers
) {
    fun processExpression(
        dest: AIRAccessPath?,
        expr: Value,  // SootUp Value (rhs of assignment)
        context: AIRSemanticsContext
    ): List<AIRStmt> = when (expr) {
        is AbstractInvokeExpr  -> invokeHandler.process(dest, expr, context)
        is AbstractBinopExpr   -> binopHandler.process(dest, expr, context)
        is JNegExpr            -> negHandler.process(dest, expr, context)
        is JLengthExpr         -> lengthHandler.process(dest, expr, context)
        is JCastExpr           -> castHandler.process(dest, expr, context)
        is JInstanceOfExpr     -> instanceOfHandler.process(dest, expr, context)
        is JNewExpr            -> newHandler.process(dest, expr, context)
        is JNewArrayExpr       -> newArrayHandler.process(dest, expr, context)
        is JNewMultiArrayExpr  -> newMultiArrayHandler.process(dest, expr, context)
        else -> emptyList()  // not an expression the DSL handles → fall through
    }

    fun processInvoke(
        dest: AIRAccessPath?,
        invokeExpr: AbstractInvokeExpr,
        context: AIRSemanticsContext
    ): List<AIRStmt> = invokeHandler.process(dest, invokeExpr, context)
}

6.8 AirTranslationSuite: Accept DSL Definition

Extend AirTranslationSuite to accept a FairAnalysisDefinition<T> and wire it into semantics:

class AirTranslationSuite<T>(
    val view: View,
    val callGraph: CallGraph,
    val analysisDefinition: FairAnalysisDefinition<T>,  // NEW
    val wholeProgramContext: WholeProgramContext,
) {
    fun lift(
        sootMethod: SootMethod,
        frameworkType: FrameworkType,
        sensitivityType: SensitivityType,
        contextStrategy: ContextStrategy,
    ): AIRBody {
        // Build DSL-driven semantics
        val heapSemantics = DefaultHeapSemantics()
        val baseCallSemantics = when (frameworkType) {
            FrameworkType.IFDS -> IFDSCallSemantics()
            FrameworkType.MONOTONE -> MonotoneCallSemantics()
            else -> IFDSCallSemantics()
        }
        val dslCallSemantics = DslCallSemantics(analysisDefinition, baseCallSemantics)
        val dslExprSemantics = DslExprSemantics(analysisDefinition)
        val dslStmtSemantics = DslStmtSemantics(
            analysisDefinition, dslExprSemantics, heapSemantics, dslCallSemantics
        )
        val semantics = DslDrivenSemantics(
            dslStmtSemantics, dslExprSemantics, heapSemantics, dslCallSemantics
        )

        // ... rest as before (context creation, AIRMethodConverter)
    }
}

6.9 Entry-Point API

The user-facing API looks like:

fun main() {
    // 1. Define analysis via DSL
    val analysis = fairAnalysis("TaintAnalysis", TaintLattice) {
        onInvokeExpr {
            source(matching { it.subSignature.name == "getParam" }) {
                taintsReturnValue()
            }
            sink(matching { it.subSignature.name == "sqlQuery" }) {
                checksArgument(0) { finding -> println("FINDING: $finding") }
            }
            sanitizer(matching { it.subSignature.name == "sanitize" }) {
                killsTaintOnReturnValue()
            }
        }
    }

    // 2. Set up SootUp
    val view = SootUpManager.createView(classPath)
    val callGraph = SootUpManager.createCallGraph(view, entryPoints)

    // 3. Convert to FAIR
    val suite = AirTranslationSuite(view, callGraph, analysis, WholeProgramContext())
    val airBody = suite.lift(method, FrameworkType.IFDS, SensitivityType.FIELDSENS, ContextStrategy.NONE)
}

7. Implementation Steps (Suggested Order)

Step 1: Project Skeleton

  • Create DSL-FAIR/ with build.gradle.kts (single module, SootUp dependencies from FAIR's build file).
  • Copy air.core source tree verbatim into src/main/kotlin/air/.

Step 2: Lattice Merge

  • Create dsl/Lattice.kt with FairDSL.kt's Lattice<T> interface (bottom, top, join, meet, leq).
  • Port DSL's FlatLattice, MapLattice, LiftLattice, ProductLattice to the new interface (add meet/leq).
  • Keep FairDSL.kt's Flat<E> sealed class and FlatLattice<E> as well (different use case — three-element flat).
  • Copy TaintLattice from FairDSL.kt.

Step 3: DomainOperation + Environment

  • Copy from FairDSL.kt: DomainOperation<T>, IdentityOp, JoinOp, SetTopOp, SetBottomOp, CheckTopOp.
  • Copy AbstractEnvironment<T>, Finding, FindingReporter.
  • Create DslOperator<T> wrapper implementing AIROperator.

Step 4: Expression Handlers (SootUp-Adapted)

  • Port FairDSL.kt's ExprHandler<T> hierarchy, replacing mock types with SootUp types.
  • Each handler's process() now takes (AIRAccessPath?, Expr/Value, AIRSemanticsContext) and returns List<AIRStmt>.
  • Handlers use LiftValueHelper to convert SootUp values to AIR values.
  • InvokeExprHandler matchers operate on real sootup.core.signatures.MethodSignature.
  • Source/sink/sanitizer effect builders unchanged (they produce DomainOperation<T> instances).

Step 5: Copy FAIR Converter Infrastructure

  • Copy all converter/, semantics/, sensitivity/, helper/, rule/ files.
  • Adjust package declarations as needed.

Step 6: DSL-Driven Semantics

  • Create DslStmtSemantics<T> (extends DefaultAIRStmtSemantics, overrides translateAssign).
  • Create DslCallSemantics<T> (wraps existing call semantics, intercepts invoke for DSL rules).
  • Create DslExprSemantics<T> (implements AIRExprSemantics, delegates to DSL binop handler).
  • Create DslDrivenSemantics<T> (replaces TaintIFDSSemantics, composes the DSL-driven pieces).

Step 7: Wire Up AirTranslationSuite

  • Modify AirTranslationSuite to accept FairAnalysisDefinition<T> and construct DslDrivenSemantics.
  • Remove AnalysisPolicyProvider dependency (or keep as fallback for non-DSL usage).

Step 8: Entry Point + Examples

  • Create Main.kt with defineTaintAnalysis() example connected to real SootUp.
  • Add example for constant propagation or typestate to validate genericity.
  • Port or create tests.

8. Summary: File Disposition Table

Source File Action
FAIR/air.core/** All files Copy as-is
FAIR/src/**/AIRMethodConverter.kt Copy as-is
FAIR/src/**/AIRWholeProgramConverter.kt Copy as-is
FAIR/src/**/AirTranslationSuite.kt Copy + extend (accept FairAnalysisDefinition)
FAIR/src/**/DefaultAIRStmtSemantics.kt Copy as-is (DslStmtSemantics extends it)
FAIR/src/**/AbstractCallSemantics.kt Copy as-is (DslCallSemantics wraps it)
FAIR/src/**/IFDSCallSemantics.kt Copy as-is (used as delegate)
FAIR/src/**/MonotoneCallSemantics.kt Copy as-is (used as delegate)
FAIR/src/**/IFDSExprSemantics.kt Copy as-is (fallback for non-DSL)
FAIR/src/**/IFDSAIRStmtSemantics.kt Copy as-is (fallback for non-DSL)
FAIR/src/**/TaintIFDSSemantics.kt Drop (replaced by DslDrivenSemantics)
FAIR/src/**/AnalysisPolicyProvider.kt Drop (replaced by DSL-driven wiring)
FAIR/src/**/AIRSemanticsContext.kt Copy as-is (no change needed)
FAIR/src/**/AIRSemantics.kt Copy as-is
FAIR/src/**/BinaryOpFinder.kt Copy as-is
FAIR/src/**/LiftValueHelper.kt Copy as-is
FAIR/src/**/AccessPathFactory.kt Copy as-is
FAIR/src/**/CallGraphIndex.kt Copy as-is
FAIR/src/**/FoldHelper.kt Copy as-is
FAIR/src/**/ConversionUtil.kt Copy as-is
FAIR/src/**/SootUpManager.kt Copy as-is
FAIR/src/**/WholeProgramContext.kt Copy as-is
FAIR/src/**/TaintMethodRule.kt Copy as-is (kept for compat, DSL replaces)
FAIR/src/**/MethodRule.kt Copy as-is
All other FAIR/src/** utils Copy as-is
FairDSL.kt §1-9, §11-15 Lattice, Ops, Handlers, Builder Adapt (replace mock types with SootUp)
FairDSL.kt §10 JimpleExpr, MethodSignature Drop (replaced by real SootUp types)
FairDSL.kt §16 main() demo Adapt (use real SootUp setup)
DSL/src/Lattice.kt FlatLattice, MapLattice, etc. Port (add meet/leq to match new interface)
DSL/src/Models.kt Pattern, Effect, FlowRule Drop
DSL/src/AnalysisBuilder.kt Drop
DSL/src/RulesBuilder.kt Drop
DSL/src/Rules.kt Drop (lattice constructors subsumed)
DSL/src/Globals.kt Drop
DSL/src/printAnalysis.kt Drop
DSL/src/Main.kt Drop
DslStmtSemantics.kt New
DslCallSemantics.kt New
DslExprSemantics.kt New
DslDrivenSemantics.kt New
DslOperator.kt New

9. Key Design Invariants to Maintain

  1. Expression-level, not statement-level: The DSL handles expressions. The converter handles statements (copy, load, store, assume, goto). DslStmtSemantics is the bridge.

  2. Two-phase callback: At conversion time, the DSL selects DomainOperations and embeds them in AIRApplyStmt. At interpretation time, the interpreter executes those operations against the abstract environment.

  3. Generic defaults: If the DSL user doesn't configure a handler, the generic default fires (IdentityOp for unary, JoinOp for binary, SetTopOp for allocation). This makes simple analyses like taint analysis very concise.

  4. First-match wins: InvokeExprHandler tries matcher rules sequentially; first match wins. No match → default propagation. This mirrors FairDSL.kt's design.

  5. Backward compatibility: Existing FAIR infrastructure (call semantics, heap semantics, fold statements, context handling) is preserved. The DSL adds an expression-level layer on top; it does not replace the structural CFG conversion or context sensitivity mechanisms.