Files
sdk/pkg/vm/lib/incremental_compiler.dart
T
Alexander Aprelev 7d14785115 This relands "Load isolate from parent's kernel in Isolate.spawn calls.".
Original PR was reverted because it broke hot-reload bots. This CL adds steps to initialize kernel_service compiler for child isolate. Compiler is needed to support hot-reload request. Child isolate's compiler is created from full kernel file produced by main isolate's compiler. Changes since original PR are pkg/vm/bin/kernel_service.dart in pkg/vm/lib/incremental_compiler.dart.

Further this CL changes kernel fingerprint calculation for interface types so it calculates the hash of the canonical names themselves, rather than indices(that might change from one compilation to another).

This reverts commit 63fd8f63e6.

Change-Id: I6fe5b2ef99f209b32cd4087dfd1c8cac229c2d8b
Reviewed-on: https://dart-review.googlesource.com/c/87265
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Liam Appelbe <liama@google.com>
2018-12-14 23:45:57 +00:00

154 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_unstable/vm.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;
Uri get entryPoint => _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 {
_entryPoint = entryPoint ?? _entryPoint;
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);
}
}