@@ -183,6 +183,59 @@ class ResidentCompiler {
183183 incrementalCompile: incremental);
184184 }
185185
186+ /// WARNING: [compile] must be called on this compiler to populate the
187+ /// required context in it before [compileExpression] can be called on it.
188+ Future <String > compileExpression (
189+ String expression,
190+ List <String > definitions,
191+ List <String > definitionTypes,
192+ List <String > typeDefinitions,
193+ List <String > typeBounds,
194+ List <String > typeDefaults,
195+ String libraryUri,
196+ String ? klass,
197+ String ? method,
198+ int offset,
199+ String ? scriptUri,
200+ bool isStatic,
201+ ) async {
202+ await _compiler.compileExpression (
203+ expression,
204+ definitions,
205+ definitionTypes,
206+ typeDefinitions,
207+ typeBounds,
208+ typeDefaults,
209+ libraryUri,
210+ klass,
211+ method,
212+ offset,
213+ scriptUri,
214+ isStatic,
215+ );
216+
217+ _compilerOutput.clear ();
218+ // [incrementalMode] can only ever be [false] if `--aot` was passed in the
219+ // 'compileExpression' request received by the [ResidentFrontendServer],
220+ // which should be impossible.
221+ assert (incrementalMode);
222+ // Force the compiler to produce complete kernel files on each request, even
223+ // when incrementally compiled.
224+ _compiler
225+ ..acceptLastDelta ()
226+ ..resetIncrementalCompiler ();
227+ resetStateToWaitingForFirstCompile ();
228+
229+ final List <String > errors = _compiler.errors;
230+ final int errorCount = errors.length;
231+ return jsonEncode ({
232+ 'success' : errorCount == 0 ,
233+ 'errorCount' : errorCount,
234+ if (errorCount > 0 ) 'compilerOutputLines' : errors,
235+ 'kernelBytes' : base64Encode (_outputDill.readAsBytesSync ()),
236+ });
237+ }
238+
186239 /// Reads the compiler's [outputLines] to keep track of which files
187240 /// need to be tracked. Adds correctly ANSI formatted output to
188241 /// the [_formattedOutput] list.
@@ -266,7 +319,23 @@ class ResidentFrontendServer {
266319 static const String _compileString = 'compile' ;
267320 static const String _executableString = 'executable' ;
268321 static const String _packageString = 'packages' ;
322+ static const String _successString = 'success' ;
269323 static const String _outputString = 'output-dill' ;
324+ static const String _compileExpressionString = 'compileExpression' ;
325+ static const String _libraryUriString = 'libraryUri' ;
326+ static const String _rootLibraryUriString = 'rootLibraryUri' ;
327+ static const String _dillExtensionString = '.dill' ;
328+ static const String _expressionString = 'expression' ;
329+ static const String _definitionsString = 'definitions' ;
330+ static const String _definitionTypesString = 'definitionTypes' ;
331+ static const String _typeDefinitionsString = 'typeDefinitions' ;
332+ static const String _typeBoundsString = 'typeBounds' ;
333+ static const String _typeDefaultsString = 'typeDefaults' ;
334+ static const String _classString = 'class' ;
335+ static const String _methodString = 'method' ;
336+ static const String _offsetString = 'offset' ;
337+ static const String _scriptUriString = 'scriptUri' ;
338+ static const String _isStaticString = 'isStatic' ;
270339 static const String _shutdownString = 'shutdown' ;
271340 static const int _compilerLimit = 3 ;
272341
@@ -341,7 +410,7 @@ class ResidentFrontendServer {
341410 }
342411
343412 return jsonEncode ({
344- "success" : true ,
413+ _successString : true ,
345414 });
346415 }
347416
@@ -368,14 +437,15 @@ class ResidentFrontendServer {
368437 final ArgResults options = _generateCompilerOptions (
369438 request: request,
370439 outputDillOverride: cachedDillPath,
440+ initializeFromDillPath: cachedDillPath,
371441 );
372442 final ResidentCompiler residentCompiler = _getResidentCompilerForEntrypoint (
373443 canonicalizedExecutablePath,
374444 options,
375445 );
376446 final Map <String , dynamic > response = await residentCompiler.compile ();
377447
378- if (response['success' ] != true ) {
448+ if (response[_successString ] != true ) {
379449 return jsonEncode (response);
380450 }
381451
@@ -392,6 +462,90 @@ class ResidentFrontendServer {
392462 return jsonEncode ({...response, _outputString: outputDillPath});
393463 }
394464
465+ static Future <String > _handleCompileExpressionRequest (
466+ Map <String , dynamic > request,
467+ ) async {
468+ final String canonicalizedLibraryPath;
469+ try {
470+ if ((request[_libraryUriString] as String ).startsWith ('dart:' )) {
471+ // An argument to the [entrypoint] parameter of
472+ // [FrontendCompiler.compile] is mandatory, and
473+ // [canonicalizedLibraryPath] is what we will use as that argument, so
474+ // if the library URI provided in the request begins with 'dart:', then
475+ // we use the URI of the root library of the isolate group in which the
476+ // evaluation is taking place to compute [canonicalizedLibraryPath]
477+ // instead.
478+ canonicalizedLibraryPath = path.canonicalize (
479+ Uri .parse (request[_rootLibraryUriString]).toFilePath (),
480+ );
481+ } else {
482+ canonicalizedLibraryPath = path
483+ .canonicalize (Uri .parse (request[_libraryUriString]).toFilePath ());
484+ }
485+ } catch (e) {
486+ return _encodeErrorMessage (
487+ "Request contains invalid '$_libraryUriString ' property" ,
488+ );
489+ }
490+
491+ final String cachedDillPath =
492+ computeCachedDillPath (canonicalizedLibraryPath);
493+ // Make the [ResidentCompiler] output the compiled expression to
494+ // [compiledExpressionDillPath] to prevent it from overwriting the
495+ // cached program dill.
496+ assert (cachedDillPath.endsWith (_dillExtensionString));
497+ final String compiledExpressionDillPath = cachedDillPath.replaceRange (
498+ cachedDillPath.length - _dillExtensionString.length,
499+ null ,
500+ '.expr.dill' ,
501+ );
502+ final ArgResults options = _generateCompilerOptions (
503+ request: request,
504+ outputDillOverride: compiledExpressionDillPath,
505+ initializeFromDillPath: cachedDillPath,
506+ );
507+
508+ final ResidentCompiler residentCompiler =
509+ _getResidentCompilerForEntrypoint (canonicalizedLibraryPath, options);
510+
511+ final String expression = request[_expressionString];
512+ final List <String > definitions =
513+ (request[_definitionsString] as List <dynamic >).cast <String >();
514+ final List <String > definitionTypes =
515+ (request[_definitionTypesString] as List <dynamic >).cast <String >();
516+ final List <String > typeDefinitions =
517+ (request[_typeDefinitionsString] as List <dynamic >).cast <String >();
518+ final List <String > typeBounds =
519+ (request[_typeBoundsString] as List <dynamic >).cast <String >();
520+ final List <String > typeDefaults =
521+ (request[_typeDefaultsString] as List <dynamic >).cast <String >();
522+ final String libraryUri = request[_libraryUriString];
523+ final String ? klass = request[_classString];
524+ final String ? method = request[_methodString];
525+ final int offset = request[_offsetString];
526+ final String ? scriptUri = request[_scriptUriString];
527+ final bool isStatic = request[_isStaticString];
528+
529+ // [residentCompiler.compile] must be called before
530+ // [residentCompiler.compileExpression] can be called. See the
531+ // documentation of [ResidentCompiler.compile] for more information.
532+ await residentCompiler.compile ();
533+ return await residentCompiler.compileExpression (
534+ expression,
535+ definitions,
536+ definitionTypes,
537+ typeDefinitions,
538+ typeBounds,
539+ typeDefaults,
540+ libraryUri,
541+ klass,
542+ method,
543+ offset,
544+ scriptUri,
545+ isStatic,
546+ );
547+ }
548+
395549 /// Takes in JSON [input] from the socket and compiles the request,
396550 /// using incremental compilation if possible. Returns a JSON string to be
397551 /// sent back to the client socket containing either an error message or the
@@ -413,6 +567,8 @@ class ResidentFrontendServer {
413567 return _handleReplaceCachedDillRequest (request);
414568 case _compileString:
415569 return _handleCompileRequest (request);
570+ case _compileExpressionString:
571+ return _handleCompileExpressionRequest (request);
416572 case _shutdownString:
417573 return _shutdownJsonResponse;
418574 default :
@@ -428,12 +584,23 @@ class ResidentFrontendServer {
428584 /// The compiled kernel file will be stored at this path, and not at
429585 /// [request['--output-dill'] ].
430586 required String outputDillOverride,
587+ required String initializeFromDillPath,
431588 }) {
432589 return argParser.parse (< String > [
433590 '--sdk-root=${_sdkUri .toFilePath ()}' ,
434591 if (! (request['aot' ] ?? false )) '--incremental' ,
435592 '--platform=${_platformKernelUri .path }' ,
436593 '--output-dill=$outputDillOverride ' ,
594+ '--initialize-from-dill=$initializeFromDillPath ' ,
595+ // We can assume that the cached dill is up-to-date when handling
596+ // 'compileExpression' requests because if dartdev was given a source file
597+ // to run, then it must have compiled it with the resident frontend
598+ // compiler, guaranteeing that the cached dill is up-to-date, and if
599+ // dartdev was given a dill file to run, then it must have used the
600+ // resident frontend compiler's 'replaceCachedDill' endpoint to update the
601+ // dill cache.
602+ if (request[_commandString] == _compileExpressionString)
603+ '--assume-initialize-from-dill-up-to-date' ,
437604 '--target=vm' ,
438605 '--filesystem-scheme' ,
439606 'org-dartlang-root' ,
@@ -459,8 +626,9 @@ class ResidentFrontendServer {
459626 }
460627
461628 /// Encodes the [message] in JSON to be sent over the socket.
462- static String _encodeErrorMessage (String message) =>
463- jsonEncode (< String , Object > {"success" : false , "errorMessage" : message});
629+ static String _encodeErrorMessage (String message) => jsonEncode (
630+ < String , Object > {_successString: false , 'errorMessage' : message},
631+ );
464632
465633 /// Used to create compile requests for the ResidentFrontendServer.
466634 /// Returns a JSON string that the resident compiler will be able to
0 commit comments