e07af58238
This is needed so that compileExpression requests are serviced from that accepted state. Bug: https://github.com/dart-lang/sdk/issues/34025 Change-Id: I592d2af50e59a721e1feb1699c6d56bcd568465f Reviewed-on: https://dart-review.googlesource.com/72540 Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
152 lines
5.4 KiB
Dart
152 lines
5.4 KiB
Dart
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
/// Defines wrapper class around incremental compiler to support
|
|
/// the flow, where incremental deltas can be rejected by VM.
|
|
import 'dart:async';
|
|
|
|
import 'package:front_end/src/api_prototype/compiler_options.dart';
|
|
import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
|
|
import 'package:kernel/kernel.dart';
|
|
|
|
const String kDebugProcedureName = ":Eval";
|
|
|
|
/// Wrapper around [IncrementalKernelGenerator] that keeps track of rejected
|
|
/// deltas and combines them together into resultant program until it is
|
|
/// accepted.
|
|
class IncrementalCompiler {
|
|
IncrementalKernelGenerator _generator;
|
|
|
|
// Component that reflect current state of the compiler, which has not
|
|
// been yet accepted by the client. Is [null] if no compilation was done
|
|
// since last accept/reject acknowledgement by the client.
|
|
Component _candidate;
|
|
// Component that reflect the state that was most recently accepted by the
|
|
// client. Is [null], if no compilation results were accepted by the client.
|
|
Component _lastKnownGood;
|
|
|
|
List<Component> _pendingDeltas;
|
|
CompilerOptions _compilerOptions;
|
|
bool initialized = false;
|
|
bool fullComponent = false;
|
|
Uri initializeFromDillUri;
|
|
Uri _entryPoint;
|
|
|
|
IncrementalCompiler(this._compilerOptions, this._entryPoint,
|
|
{this.initializeFromDillUri}) {
|
|
_generator = new IncrementalKernelGenerator(
|
|
_compilerOptions, _entryPoint, initializeFromDillUri);
|
|
_pendingDeltas = <Component>[];
|
|
}
|
|
|
|
/// Recompiles invalidated files, produces incremental component.
|
|
///
|
|
/// If [entryPoint] is specified, that points to new entry point for the
|
|
/// compilation. Otherwise, previously set entryPoint is used.
|
|
Future<Component> compile({Uri entryPoint}) async {
|
|
Component component = await _generator.computeDelta(
|
|
entryPoint: entryPoint, fullComponent: fullComponent);
|
|
initialized = true;
|
|
fullComponent = false;
|
|
final bool firstDelta = _pendingDeltas.isEmpty;
|
|
_pendingDeltas.add(component);
|
|
if (!firstDelta) {
|
|
// If more than one delta is pending, we need to combine them.
|
|
Procedure mainMethod;
|
|
Map<Uri, Library> combined = <Uri, Library>{};
|
|
for (Component delta in _pendingDeltas) {
|
|
if (delta.mainMethod != null) {
|
|
mainMethod = delta.mainMethod;
|
|
}
|
|
for (Library library in delta.libraries) {
|
|
combined[library.importUri] = library;
|
|
}
|
|
}
|
|
// TODO(vegorov) this needs to merge metadata repositories from deltas.
|
|
component = new Component(libraries: combined.values.toList())
|
|
..mainMethod = mainMethod;
|
|
}
|
|
_candidate = component;
|
|
return component;
|
|
}
|
|
|
|
/// This lets incremental compiler know that results of last [compile] call
|
|
/// were accepted, don't need to be included into subsequent [compile] calls
|
|
/// results.
|
|
accept() {
|
|
_pendingDeltas.clear();
|
|
|
|
Map<Uri, Library> combined = <Uri, Library>{};
|
|
if (_lastKnownGood != null) {
|
|
// TODO(aam): Figure out how to skip no-longer-used libraries from
|
|
// [_lastKnownGood] libraries.
|
|
for (Library library in _lastKnownGood.libraries) {
|
|
combined[library.importUri] = library;
|
|
}
|
|
}
|
|
for (Library library in _candidate.libraries) {
|
|
combined[library.importUri] = library;
|
|
}
|
|
_lastKnownGood = new Component(
|
|
libraries: combined.values.toList(),
|
|
uriToSource: _candidate.uriToSource,
|
|
)..mainMethod = _candidate.mainMethod;
|
|
for (final repo in _candidate.metadata.values) {
|
|
_lastKnownGood.addMetadataRepository(repo);
|
|
}
|
|
|
|
_candidate = null;
|
|
}
|
|
|
|
/// This lets incremental compiler know that results of last [compile] call
|
|
/// were rejected. Subsequent [compile] or [compileExpression] calls need to
|
|
/// be processed without changes picked up by rejected [compile] call.
|
|
reject() async {
|
|
_pendingDeltas.clear();
|
|
_candidate = null;
|
|
// Need to reset and warm up compiler so that expression evaluation requests
|
|
// are processed in that known good state.
|
|
_generator = new IncrementalKernelGenerator.fromComponent(
|
|
_compilerOptions, _entryPoint, _lastKnownGood);
|
|
await _generator.computeDelta(entryPoint: _entryPoint);
|
|
}
|
|
|
|
/// This tells incremental compiler that it needs rescan [uri] file during
|
|
/// next [compile] call.
|
|
invalidate(Uri uri) {
|
|
_generator.invalidate(uri);
|
|
}
|
|
|
|
resetDeltaState() {
|
|
_pendingDeltas.clear();
|
|
fullComponent = true;
|
|
}
|
|
|
|
Future<Procedure> compileExpression(
|
|
String expression,
|
|
List<String> definitions,
|
|
List<String> typeDefinitions,
|
|
String libraryUri,
|
|
String klass,
|
|
bool isStatic) {
|
|
Map<String, DartType> completeDefinitions = {};
|
|
for (String name in definitions) {
|
|
if (!isLegalIdentifier(name)) continue;
|
|
completeDefinitions[name] = new DynamicType();
|
|
}
|
|
|
|
List<TypeParameter> typeParameters = [];
|
|
for (String name in typeDefinitions) {
|
|
if (!isLegalIdentifier(name)) continue;
|
|
typeParameters.add(new TypeParameter(name, new DynamicType()));
|
|
}
|
|
|
|
Uri library = Uri.parse(libraryUri);
|
|
if (library == null) return null;
|
|
|
|
return _generator.compileExpression(expression, completeDefinitions,
|
|
typeParameters, kDebugProcedureName, library, klass, isStatic);
|
|
}
|
|
}
|