From e7d8b99bb623ecd5f143152b85606e85b0592632 Mon Sep 17 00:00:00 2001 From: Nicholas Shahan Date: Tue, 10 Feb 2026 16:58:36 -0800 Subject: [PATCH] [ddc] Use LibraryBundleCompiler in expression eval When the canary and ddc modules flags are passed together use the expression compiler worker uses the LibraryBundleCompiler to compile the expression to be evaluated. This ensures the compiler JavaScript matches the expected format and representation of the compiled library. Change-Id: I4667522a6640707265a3f38770a17fd696c840f4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/479400 Reviewed-by: Mark Zhou Commit-Queue: Nicholas Shahan --- .../kernel/expression_compiler_worker.dart | 53 ++++--- .../expression_compiler_worker_shared.dart | 145 +++++++++++++++--- 2 files changed, 160 insertions(+), 38 deletions(-) diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart index a9eaee02c43..92318bed026 100644 --- a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart +++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart @@ -25,6 +25,7 @@ import '../compiler/js_names.dart'; import '../compiler/module_builder.dart' show ModuleFormat, parseModuleFormat; import 'asset_file_system.dart'; import 'compiler.dart' show ProgramCompiler; +import 'compiler_new.dart' show LibraryBundleCompiler; import 'expression_compiler.dart' show ExpressionCompiler; import 'target.dart' show DevCompilerTarget; @@ -497,22 +498,40 @@ class ExpressionCompilerWorker { var coreTypes = incrementalCompilerResult.coreTypes; var hierarchy = incrementalCompilerResult.classHierarchy; - var kernel2jsCompiler = ProgramCompiler( - finalComponent, - hierarchy, - Options( - sourceMap: true, - summarizeApi: false, - moduleName: moduleName, - canaryFeatures: _canaryFeatures, - enableAsserts: _enableAsserts, - ), - _moduleCache.componentForLibrary, - _moduleCache.moduleNameForComponent, - coreTypes: coreTypes, - ticker: _processedOptions.ticker, + var options = Options( + sourceMap: true, + summarizeApi: false, + moduleName: moduleName, + moduleFormats: [_moduleFormat], + canaryFeatures: _canaryFeatures, + enableAsserts: _enableAsserts, ); - + if (isSdk && options.emitLibraryBundle) { + errors.add( + 'Expression evaluation in the context of an SDK library ' + 'is not currently supported in this environment.', + ); + return null; + } + var kernel2jsCompiler = options.emitLibraryBundle + ? LibraryBundleCompiler( + finalComponent, + hierarchy, + options, + _moduleCache.componentForLibrary, + _moduleCache.moduleNameForComponent, + coreTypes: coreTypes, + ticker: _processedOptions.ticker, + ) + : ProgramCompiler( + finalComponent, + hierarchy, + options, + _moduleCache.componentForLibrary, + _moduleCache.moduleNameForComponent, + coreTypes: coreTypes, + ticker: _processedOptions.ticker, + ); assert( originalComponent.libraries.toSet().length == originalComponent.libraries.length, @@ -538,9 +557,7 @@ class ExpressionCompilerWorker { expressionCompiler = ExpressionCompiler( _compilerOptions, - _moduleFormat == ModuleFormat.ddc && _canaryFeatures - ? ModuleFormat.ddcLibraryBundle - : _moduleFormat, + options.emitLibraryBundle ? ModuleFormat.ddcLibraryBundle : _moduleFormat, errors, incrementalCompiler, kernel2jsCompiler, diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart index 781ad559b0e..54b89b9a303 100644 --- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart +++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_shared.dart @@ -125,16 +125,30 @@ void runExpressionCompilationTests(ExpressionCompilerWorkerTestDriver driver) { driver.responseController.stream, emitsInOrder([ equals({'succeeded': true}), - equals({ - 'succeeded': true, - 'errors': isEmpty, - 'warnings': isEmpty, - 'infos': isEmpty, - 'compiledProcedure': stringContainsInOrder([ - 'developer', - 'postEvent', - ]), - }), + // TODO(nshahan): https://github.com/dart-lang/sdk/issues/62643 + if (driver.setup.canaryFeatures && + driver.setup.moduleFormat == ModuleFormat.ddc) + equals({ + 'succeeded': false, + 'errors': [ + 'Expression evaluation in the context of an SDK library ' + 'is not currently supported in this environment.', + ], + 'warnings': [], + 'infos': [], + 'compiledProcedure': null, + }) + else + equals({ + 'succeeded': true, + 'errors': isEmpty, + 'warnings': isEmpty, + 'infos': isEmpty, + 'compiledProcedure': stringContainsInOrder([ + 'developer', + 'postEvent', + ]), + }), ]), ); }); @@ -161,16 +175,30 @@ void runExpressionCompilationTests(ExpressionCompilerWorkerTestDriver driver) { driver.responseController.stream, emitsInOrder([ equals({'succeeded': true}), - equals({ - 'succeeded': true, - 'errors': isEmpty, - 'warnings': isEmpty, - 'infos': isEmpty, - 'compiledProcedure': stringContainsInOrder([ - 'developer', - 'postEvent', - ]), - }), + // TODO(nshahan): https://github.com/dart-lang/sdk/issues/62643 + if (driver.setup.canaryFeatures && + driver.setup.moduleFormat == ModuleFormat.ddc) + equals({ + 'succeeded': false, + 'errors': [ + 'Expression evaluation in the context of an SDK library ' + 'is not currently supported in this environment.', + ], + 'warnings': [], + 'infos': [], + 'compiledProcedure': null, + }) + else + equals({ + 'succeeded': true, + 'errors': isEmpty, + 'warnings': isEmpty, + 'infos': isEmpty, + 'compiledProcedure': stringContainsInOrder([ + 'developer', + 'postEvent', + ]), + }), ]), ); }); @@ -366,6 +394,48 @@ void runExpressionCompilationTests(ExpressionCompilerWorkerTestDriver driver) { }, ); + test( + 'compile expressions include correct import format for module system', + () { + driver.requestController.add({ + 'command': 'UpdateDeps', + 'inputs': driver.inputs, + }); + + driver.requestController.add({ + 'command': 'CompileExpression', + 'expression': '5 is int && 5.isOdd', + 'line': 5, + 'column': 1, + 'jsModules': {}, + 'jsScope': {'formal': 'formal'}, + 'libraryUri': driver.config.getModule('testModule').libraryUris.first, + 'moduleName': driver.config.getModule('testModule').moduleName, + }); + + expect( + driver.responseController.stream, + emitsInOrder([ + equals({'succeeded': true}), + equals({ + 'succeeded': true, + 'errors': isEmpty, + 'warnings': isEmpty, + 'infos': isEmpty, + 'compiledProcedure': driver.setup.emitLibraryBundle + ? StringContainsUnordered([ + '"dart:core"', + '"dart:_runtime"', + '"dart:_rti"', + '"dartx"', + ]) + : contains('\'dart_sdk\''), + }), + ]), + ); + }, + ); + test('can compile expressions in main', () { driver.requestController.add({ 'command': 'UpdateDeps', @@ -1685,3 +1755,38 @@ Future runProcess( return await process.exitCode; } + +/// A matcher that checks if a string contains all of the expected substrings in +/// any order. +class StringContainsUnordered extends Matcher { + final List _expected; + + StringContainsUnordered(this._expected); + + @override + bool matches(item, Map matchState) { + if (item is! String) return false; + for (var expected in _expected) { + if (!item.contains(expected)) return false; + } + return true; + } + + @override + Description describe(Description description) => + description.add('contains all of ').addDescriptionOf(_expected); + + @override + Description describeMismatch( + item, + Description mismatchDescription, + Map matchState, + bool verbose, + ) { + if (item is! String) { + return mismatchDescription.add('is not a string'); + } + var missing = _expected.where((e) => !item.contains(e)).toList(); + return mismatchDescription.add('is missing ').addDescriptionOf(missing); + } +}