Skip to content

Commit 3a0d837

Browse files
author
Anna Gringauze
authored
Enable expression evaluation in debugger for web platform (flutter#53595)
1 parent 191f86e commit 3a0d837

File tree

11 files changed

+350
-4
lines changed

11 files changed

+350
-4
lines changed

packages/flutter_tools/lib/src/build_runner/devfs_web.dart

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@ import '../globals.dart' as globals;
3636
import '../web/bootstrap.dart';
3737
import '../web/chrome.dart';
3838

39+
/// An expression compiler connecting to FrontendServer
40+
///
41+
/// This is only used in development mode
42+
class WebExpressionCompiler implements ExpressionCompiler {
43+
WebExpressionCompiler(this._generator);
44+
45+
final ResidentCompiler _generator;
46+
47+
@override
48+
Future<ExpressionCompilationResult> compileExpressionToJs(
49+
String isolateId,
50+
String libraryUri,
51+
int line,
52+
int column,
53+
Map<String, String> jsModules,
54+
Map<String, String> jsFrameValues,
55+
String moduleName,
56+
String expression,
57+
) async {
58+
final CompilerOutput compilerOutput = await _generator.compileExpressionToJs(libraryUri,
59+
line, column, jsModules, jsFrameValues, moduleName, expression);
60+
61+
if (compilerOutput != null && compilerOutput.outputFilename != null) {
62+
final String content = utf8.decode(
63+
globals.fs.file(compilerOutput.outputFilename).readAsBytesSync());
64+
return ExpressionCompilationResult(
65+
content, compilerOutput.errorCount > 0);
66+
}
67+
68+
throw Exception('Failed to compile $expression');
69+
}
70+
}
71+
3972
/// A web server which handles serving JavaScript and assets.
4073
///
4174
/// This is only used in development mode.
@@ -85,7 +118,8 @@ class WebAssetServer implements AssetReader {
85118
UrlTunneller urlTunneller,
86119
BuildMode buildMode,
87120
bool enableDwds,
88-
Uri entrypoint, {
121+
Uri entrypoint,
122+
ExpressionCompiler expressionCompiler, {
89123
bool testMode = false,
90124
}) async {
91125
try {
@@ -170,6 +204,7 @@ class WebAssetServer implements AssetReader {
170204
serverPathForModule,
171205
serverPathForAppUri,
172206
),
207+
expressionCompiler: expressionCompiler
173208
);
174209
shelf.Pipeline pipeline = const shelf.Pipeline();
175210
if (enableDwds) {
@@ -501,6 +536,7 @@ class WebDevFS implements DevFS {
501536
@required this.buildMode,
502537
@required this.enableDwds,
503538
@required this.entrypoint,
539+
@required this.expressionCompiler,
504540
this.testMode = false,
505541
});
506542

@@ -512,6 +548,7 @@ class WebDevFS implements DevFS {
512548
final BuildMode buildMode;
513549
final bool enableDwds;
514550
final bool testMode;
551+
final ExpressionCompiler expressionCompiler;
515552

516553
WebAssetServer webAssetServer;
517554

@@ -572,6 +609,7 @@ class WebDevFS implements DevFS {
572609
buildMode,
573610
enableDwds,
574611
entrypoint,
612+
expressionCompiler,
575613
testMode: testMode,
576614
);
577615
_baseUri = Uri.parse('http://$hostname:$port');

packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
421421
buildMode: debuggingOptions.buildInfo.mode,
422422
enableDwds: _enableDwds,
423423
entrypoint: globals.fs.file(target).uri,
424+
expressionCompiler: WebExpressionCompiler(device.generator),
424425
);
425426
final Uri url = await device.devFS.create();
426427
if (debuggingOptions.buildInfo.isDebug) {

packages/flutter_tools/lib/src/codegen.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler {
160160
return _residentCompiler.compileExpression(expression, definitions, typeDefinitions, libraryUri, klass, isStatic);
161161
}
162162

163+
@override
164+
Future<CompilerOutput> compileExpressionToJs(
165+
String libraryUri, int line, int column, Map<String, String> jsModules,
166+
Map<String, String> jsFrameValues, String moduleName, String expression
167+
) {
168+
return _residentCompiler.compileExpressionToJs(
169+
libraryUri, line, column, jsModules, jsFrameValues, moduleName, expression);
170+
}
171+
163172
@override
164173
Future<CompilerOutput> recompile(String mainPath, List<Uri> invalidatedFiles, {String outputPath, String packagesFilePath}) async {
165174
if (_codegenDaemon.lastStatus != CodegenStatus.Succeeded && _codegenDaemon.lastStatus != CodegenStatus.Failed) {

packages/flutter_tools/lib/src/commands/update_packages.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const Map<String, String> _kManuallyPinnedDependencies = <String, String>{
2626
'mockito': '^4.1.0', // Prevent mockito from downgrading to 4.0.0
2727
'vm_service_client': '0.2.6+2', // Final version before being marked deprecated.
2828
'video_player': '0.10.6', // 0.10.7 fails a gallery smoke test for toString.
29-
'package_config': '1.9.1'
29+
'package_config': '1.9.1',
30+
'flutter_template_images': '1.0.0', // 1.0.1 breaks windows tests
3031
};
3132

3233
class UpdatePackagesCommand extends FlutterCommand {

packages/flutter_tools/lib/src/compile.dart

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,31 @@ class _CompileExpressionRequest extends _CompilationRequest {
431431
compiler._compileExpression(this);
432432
}
433433

434+
class _CompileExpressionToJsRequest extends _CompilationRequest {
435+
_CompileExpressionToJsRequest(
436+
Completer<CompilerOutput> completer,
437+
this.libraryUri,
438+
this.line,
439+
this.column,
440+
this.jsModules,
441+
this.jsFrameValues,
442+
this.moduleName,
443+
this.expression,
444+
) : super(completer);
445+
446+
final String libraryUri;
447+
final int line;
448+
final int column;
449+
final Map<String, String> jsModules;
450+
final Map<String, String> jsFrameValues;
451+
final String moduleName;
452+
final String expression;
453+
454+
@override
455+
Future<CompilerOutput> _run(DefaultResidentCompiler compiler) async =>
456+
compiler._compileExpressionToJs(this);
457+
}
458+
434459
class _RejectRequest extends _CompilationRequest {
435460
_RejectRequest(Completer<CompilerOutput> completer) : super(completer);
436461

@@ -489,6 +514,36 @@ abstract class ResidentCompiler {
489514
bool isStatic,
490515
);
491516

517+
/// Compiles [expression] in [libraryUri] at [line]:[column] to JavaScript
518+
/// in [moduleName].
519+
///
520+
/// Values listed in [jsFrameValues] are substituted for their names in the
521+
/// [expression].
522+
///
523+
/// Ensures that all [jsModules] are loaded and accessible inside the
524+
/// expression.
525+
///
526+
/// Example values of parameters:
527+
/// [moduleName] is of the form '/packages/hello_world_main.dart'
528+
/// [jsFrameValues] is a map from js variable name to its primitive value
529+
/// or another variable name, for example
530+
/// { 'x': '1', 'y': 'y', 'o': 'null' }
531+
/// [jsModules] is a map from variable name to the module name, where
532+
/// variable name is the name originally used in JavaScript to contain the
533+
/// module object, for example:
534+
/// { 'dart':'dart_sdk', 'main': '/packages/hello_world_main.dart' }
535+
/// Returns a [CompilerOutput] including the name of the file containing the
536+
/// compilation result and a number of errors
537+
Future<CompilerOutput> compileExpressionToJs(
538+
String libraryUri,
539+
int line,
540+
int column,
541+
Map<String, String> jsModules,
542+
Map<String, String> jsFrameValues,
543+
String moduleName,
544+
String expression,
545+
);
546+
492547
/// Should be invoked when results of compilation are accepted by the client.
493548
///
494549
/// Either [accept] or [reject] should be called after every [recompile] call.
@@ -778,6 +833,52 @@ class DefaultResidentCompiler implements ResidentCompiler {
778833
return _stdoutHandler.compilerOutput.future;
779834
}
780835

836+
@override
837+
Future<CompilerOutput> compileExpressionToJs(
838+
String libraryUri,
839+
int line,
840+
int column,
841+
Map<String, String> jsModules,
842+
Map<String, String> jsFrameValues,
843+
String moduleName,
844+
String expression,
845+
) {
846+
if (!_controller.hasListener) {
847+
_controller.stream.listen(_handleCompilationRequest);
848+
}
849+
850+
final Completer<CompilerOutput> completer = Completer<CompilerOutput>();
851+
_controller.add(
852+
_CompileExpressionToJsRequest(
853+
completer, libraryUri, line, column, jsModules, jsFrameValues, moduleName, expression)
854+
);
855+
return completer.future;
856+
}
857+
858+
Future<CompilerOutput> _compileExpressionToJs(_CompileExpressionToJsRequest request) async {
859+
_stdoutHandler.reset(suppressCompilerMessages: true, expectSources: false);
860+
861+
// 'compile-expression-to-js' should be invoked after compiler has been started,
862+
// program was compiled.
863+
if (_server == null) {
864+
return null;
865+
}
866+
867+
final String inputKey = Uuid().generateV4();
868+
_server.stdin.writeln('compile-expression-to-js $inputKey');
869+
_server.stdin.writeln(request.libraryUri ?? '');
870+
_server.stdin.writeln(request.line);
871+
_server.stdin.writeln(request.column);
872+
request.jsModules?.forEach((String k, String v) { _server.stdin.writeln('$k:$v'); });
873+
_server.stdin.writeln(inputKey);
874+
request.jsFrameValues?.forEach((String k, String v) { _server.stdin.writeln('$k:$v'); });
875+
_server.stdin.writeln(inputKey);
876+
_server.stdin.writeln(request.moduleName ?? '');
877+
_server.stdin.writeln(request.expression ?? '');
878+
879+
return _stdoutHandler.compilerOutput.future;
880+
}
881+
781882
@override
782883
void accept() {
783884
if (_compileRequestNeedsConfirmation) {

packages/flutter_tools/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ dependencies:
1111
# To update these, use "flutter update-packages --force-upgrade".
1212
archive: 2.0.13
1313
args: 1.6.0
14-
dwds: 3.0.1
14+
dwds: 3.0.2
1515
completion: 0.2.2
1616
coverage: 0.13.9
1717
crypto: 2.1.4
@@ -112,4 +112,4 @@ dartdoc:
112112
# Exclude this package from the hosted API docs.
113113
nodoc: true
114114

115-
# PUBSPEC CHECKSUM: 65c1
115+
# PUBSPEC CHECKSUM: 53c2

packages/flutter_tools/test/general.shard/web/devfs_web_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ void main() {
373373
enableDwds: false,
374374
entrypoint: Uri.base,
375375
testMode: true,
376+
expressionCompiler: null,
376377
);
377378
webDevFS.requireJS.createSync(recursive: true);
378379
webDevFS.stackTraceMapper.createSync(recursive: true);

0 commit comments

Comments
 (0)