executeBundle method

dynamic executeBundle(
  1. AstBundle bundle, {
  2. String? entryPoint,
  3. String name = 'main',
  4. List<Object?>? positionalArgs,
  5. Map<String, Object?>? namedArgs,
})

Execute an AstBundle with full import resolution.

Unlike execute, this method supports cross-module imports by creating an AstModuleLoader that resolves imports from the bundle's module map.

The entry point module is determined by:

  1. The entryPoint parameter (if provided)
  2. The bundle's AstBundle.entryPointUri

Example

final runner = D4rtRunner();
// register bridges...
final bundle = AstBundle.fromFile('app.d4rtbundle');
final result = runner.executeBundle(bundle);

Implementation

dynamic executeBundle(
  AstBundle bundle, {
  String? entryPoint,
  String name = 'main',
  List<Object?>? positionalArgs,
  Map<String, Object?>? namedArgs,
}) {
  // Determine entry point URI
  final entryUri = entryPoint ?? bundle.entryPointUri;
  final entryAst = bundle.modules[entryUri];
  if (entryAst == null) {
    throw ArgumentD4rtException(
      'Entry point "$entryUri" not found in bundle. '
      'Available: ${bundle.modules.keys.join(", ")}',
    );
  }

  // Initialize environment
  final swInit = D4rtProfiler.enabled ? (Stopwatch()..start()) : null;
  InterpretedFunction.clearParentMap();
  final executionEnvironment = _initEnvironment();
  if (D4rtProfiler.enabled) {
    D4rtProfiler.record(
        'executeBundle._initEnvironment', swInit!.elapsedMicroseconds);
  }

  // Create AstModuleLoader for import resolution
  final swLoader = D4rtProfiler.enabled ? (Stopwatch()..start()) : null;
  // PERF step #2: reuse bridged-module envs across executions, built under
  // the shared warm parent (the execution environment's enclosing). Gated by
  // [reuseAcrossRuns]:
  //  * Migrated (non-empty allowed-set) runners share envs process-wide via
  //    [_bridgedModuleEnvCache], keyed by the allowed-set signature.
  //  * Legacy runners reuse a *per-instance* [_instanceBridgedModuleEnvCache]
  //    — never shared across runners, so their `<default>`-package bridges
  //    cannot substitute into another runner.
  // When [reuseAcrossRuns] is false both stay null → the loader rebuilds the
  // module envs fresh every run for full inter-run isolation.
  Map<String, Environment>? sharedBridgedModuleEnvs;
  Environment? sharedModuleEnclosing;
  if (reuseAcrossRuns) {
    if (_allowedPackages.isNotEmpty) {
      final key = (_allowedPackages.toList()..sort()).join('|');
      sharedBridgedModuleEnvs =
          _bridgedModuleEnvCache.putIfAbsent(key, () => {});
    } else {
      sharedBridgedModuleEnvs = _instanceBridgedModuleEnvCache ??= {};
    }
    sharedModuleEnclosing = executionEnvironment.enclosing;
  }
  final moduleLoader = AstModuleLoader(
    modules: bundle.modules,
    globalEnvironment: executionEnvironment,
    runner: this,
    sharedBridgedModuleEnvironments: sharedBridgedModuleEnvs,
    sharedModuleEnclosing: sharedModuleEnclosing,
    onBridgedModuleEnvBuilt: () => _debugBridgedModuleEnvBuilds++,
  );
  moduleLoader.currentLibrary = Uri.parse(entryUri);
  // Step #3 (retention) — track the current run's loader so dispose() can
  // release its parsed-module cache and debugLoadedModuleCount can observe it.
  _lastModuleLoader = moduleLoader;
  if (D4rtProfiler.enabled) {
    D4rtProfiler.record(
        'executeBundle.moduleLoader', swLoader!.elapsedMicroseconds);
  }

  Logger.debug(
    '[D4rtRunner.executeBundle] Entry point: $entryUri '
    '(${bundle.modules.length} modules in bundle)',
  );

  // Execute with full module context
  return _executeInEnvironment(
    compilationUnit: entryAst,
    executionEnvironment: executionEnvironment,
    moduleContext: moduleLoader,
    name: name,
    positionalArgs: positionalArgs,
    namedArgs: namedArgs,
  );
}