Skip to main content

Overview

The PassRunner class runs a list of obfuscation passes over a Dart kernel Component. It creates a shared PassContext that all passes can access to coordinate symbol table updates, name generation, and configuration options.

Constructor

PassRunner({
  required List<Pass> passes,
  Logger? logger,
})
passes
List<Pass>
required
Ordered list of obfuscation passes to run. Passes execute sequentially in the order provided.
logger
Logger?
Optional Mason logger for logging pass execution progress

Methods

run()

Runs all passes on the given component and returns the modified component along with the symbol table.
(Component, SymbolTable) run(
  Component component,
  PassOptions options,
)
component
Component
required
The kernel Component to obfuscate. This is mutated in-place by the passes.
options
PassOptions
required
Configuration options for all passes, including exclude patterns and feature flags
(Component, SymbolTable)
Record
Returns a tuple containing:
  • The modified kernel Component (same instance, mutated in-place)
  • The SymbolTable with all recorded identifier mappings

Supporting Classes

PassContext

Shared mutable context passed to every obfuscation pass.
class PassContext {
  PassContext({
    required SymbolTable symbolTable,
    required NameGenerator nameGenerator,
    required PassOptions options,
  });
  
  final SymbolTable symbolTable;
  final NameGenerator nameGenerator;
  final PassOptions options;
  
  bool shouldObfuscateLibrary(Library library);
}
symbolTable
SymbolTable
required
Shared symbol table for recording identifier mappings
nameGenerator
NameGenerator
required
Shared name generator for producing obfuscated identifiers
options
PassOptions
required
Configuration options including exclusion patterns and feature flags

shouldObfuscateLibrary()

Determines if a library should be obfuscated based on its import URI.
bool shouldObfuscateLibrary(Library library)
Returns false for:
  • SDK libraries (dart:*)
  • Libraries matching excludeLibraryUriPatterns
  • Third-party packages (only project package is obfuscated)

PassOptions

Configuration options for the obfuscation engine.
class PassOptions {
  const PassOptions({
    List<Glob> excludeLibraryUriPatterns = const [],
    bool preserveMain = true,
    List<RegExp> stringExcludePatterns = const [],
    bool verbose = false,
  });
}
excludeLibraryUriPatterns
List<Glob>
default:"[]"
URI/path glob patterns for libraries to skip entirely
preserveMain
bool
default:"true"
If true, the top-level main procedure is not renamed
stringExcludePatterns
List<RegExp>
default:"[]"
Regex patterns for string literals that should not be encrypted
verbose
bool
default:"false"
If true, enable verbose logging

Pass

Base class for all obfuscation passes.
abstract class Pass {
  String get name;
  void run(Component component, PassContext context);
}
name
String
required
Human-readable name used in logging and CLI --passes flag
Passes must implement run() to mutate the component in-place using the shared context.

Usage Example

import 'package:kernel/kernel.dart';
import 'package:refractor/refractor.dart';

void main() {
  // Load kernel component
  final component = loadComponentFromBinary('app.dill');
  
  // Create passes
  final passes = [
    RenamePass(),
    StringEncryptPass(),
    DeadCodePass(),
  ];
  
  // Create runner
  final runner = PassRunner(
    passes: passes,
    logger: Logger(),
  );
  
  // Run obfuscation
  final (obfuscated, symbolTable) = runner.run(
    component,
    PassOptions(
      excludeLibraryUriPatterns: [Glob('package:flutter/**')],
      preserveMain: true,
    ),
  );
  
  // Use results
  writeComponentToBinary(obfuscated, 'app.obfuscated.dill');
  symbolTable.writeToFile('symbols.json');
  
  print('Obfuscation complete!');
  print('Mapped ${symbolTable.size} symbols');
}
Passes are executed sequentially in the order provided. Each pass sees the modifications made by previous passes.
The PassContext provides a shouldObfuscateLibrary() helper that automatically excludes SDK libraries, third-party packages, and libraries matching your exclude patterns.

Creating Custom Passes

Extend the Pass base class to create your own obfuscation transformation:
class MyCustomPass extends Pass {
  @override
  String get name => 'my-custom-pass';
  
  @override
  void run(Component component, PassContext context) {
    for (final library in component.libraries) {
      if (!context.shouldObfuscateLibrary(library)) continue;
      
      // Transform the library
      for (final class_ in library.classes) {
        // Your transformation logic here
        final newName = context.nameGenerator.next();
        context.symbolTable.record(class_.name, newName);
        class_.name = newName;
      }
    }
  }
}
Always check shouldObfuscateLibrary() before modifying a library to avoid breaking SDK or third-party code.

RefractorEngine

High-level engine that uses PassRunner internally

SymbolTable

Tracks identifier mappings produced by passes

Build docs developers (and LLMs) love