Skip to content

Commit

Permalink
Issue 31167. Add IncrementalKernelGenerator.setState().
Browse files Browse the repository at this point in the history
R=ahe@google.com, paulberry@google.com, sigmund@google.com

Bug: #31167
Change-Id: I48971fc86c440fec636a775fc68d4292b0309dfa
Reviewed-on: https://dart-review.googlesource.com/15649
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
  • Loading branch information
scheglov authored and commit-bot@chromium.org committed Oct 20, 2017
1 parent 52b93fb commit ed69540
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 18 deletions.
20 changes: 15 additions & 5 deletions pkg/front_end/lib/incremental_kernel_generator.dart
Expand Up @@ -27,7 +27,13 @@ typedef Future<Null> WatchUsedFilesFn(Uri uri, bool used);
///
/// Not intended to be implemented or extended by clients.
class DeltaProgram {
/// The new state of the program.
/// The state of the program.
///
/// It should be treated as opaque data by the clients. Its only purpose is
/// to be passed to [IncrementalKernelGeneratorImpl.setState].
final String state;

/// The new program.
///
/// It includes full kernels for changed libraries and for libraries that
/// are affected by the transitive change of API in the changed libraries.
Expand All @@ -40,9 +46,7 @@ class DeltaProgram {
/// modified or affected.
final Program newProgram;

DeltaProgram(this.newProgram);

/// TODO(paulberry): add information about libraries that were removed.
DeltaProgram(this.state, this.newProgram);
}

/// Interface for generating an initial kernel representation of a program and
Expand Down Expand Up @@ -107,7 +111,7 @@ abstract class IncrementalKernelGenerator {
void invalidate(Uri uri);

/// Notify the generator that the last [DeltaProgram] returned from the
/// [computeDelta] was rejected. The "last program state" is discared and
/// [computeDelta] was rejected. The "last program state" is discarded and
/// the "current program state" is kept unchanged.
void rejectLastDelta();

Expand All @@ -118,6 +122,12 @@ abstract class IncrementalKernelGenerator {
/// [computeDelta] invocation.
void reset();

/// Set the "current program state", so that the next invocation of
/// [computeDelta] will include only libraries changed since this [state].
///
/// The [state] must be a value returned in [DeltaProgram.state].
void setState(String state);

/// Creates an [IncrementalKernelGenerator] which is prepared to generate
/// kernel representations of the program whose main library is in the given
/// [entryPoint].
Expand Down
36 changes: 35 additions & 1 deletion pkg/front_end/lib/src/incremental_kernel_generator_impl.dart
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:convert';

import 'package:front_end/incremental_kernel_generator.dart';
import 'package:front_end/src/base/performace_logger.dart';
Expand Down Expand Up @@ -154,7 +155,8 @@ class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
}
}

return new DeltaProgram(program);
var stateString = _ExternalState.asString(_lastSignatures);
return new DeltaProgram(stateString, program);
} finally {
_isComputeDeltaExecuting = false;
}
Expand All @@ -178,6 +180,16 @@ class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
_lastSignatures = null;
}

@override
void setState(String state) {
if (_isComputeDeltaExecuting) {
throw new StateError(MSG_PENDING_COMPUTE);
}
var signatures = _ExternalState.fromString(state);
_currentSignatures.clear();
_currentSignatures.addAll(signatures);
}

/// Find files which are not referenced from the entry point and report
/// them to the watch function.
Future<Null> _gc() async {
Expand All @@ -202,6 +214,28 @@ class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
}
}

class _ExternalState {
/// Return the JSON encoding of the [signatures].
static String asString(Map<Uri, String> signatures) {
var json = <String, String>{};
signatures.forEach((uri, signature) {
json[uri.toString()] = signature;
});
return JSON.encode(json);
}

/// Decode the given JSON [state] into the program state.
static Map<Uri, String> fromString(String state) {
var signatures = <Uri, String>{};
Map<String, String> json = JSON.decode(state);
json.forEach((uriStr, signature) {
var uri = Uri.parse(uriStr);
signatures[uri] = signature;
});
return signatures;
}
}

@visibleForTesting
class _TestView {
final IncrementalKernelGeneratorImpl _generator;
Expand Down
79 changes: 67 additions & 12 deletions pkg/front_end/test/incremental_kernel_generator_test.dart
Expand Up @@ -36,10 +36,11 @@ class IncrementalKernelGeneratorTest {
IncrementalKernelGeneratorImpl incrementalKernelGenerator;

/// Compute the initial [Program] for the given [entryPoint].
Future<Program> getInitialState(Uri entryPoint,
Future<DeltaProgram> getInitialState(Uri entryPoint,
{Uri sdkOutlineUri,
bool setPackages: true,
bool embedSourceText: true}) async {
bool embedSourceText: true,
String initialState}) async {
createSdkFiles(fileSystem);
// TODO(scheglov) Builder the SDK kernel and set it into the options.

Expand All @@ -56,9 +57,15 @@ class IncrementalKernelGeneratorTest {
if (setPackages) {
compilerOptions.packagesFileUri = Uri.parse('file:///test/.packages');
}

incrementalKernelGenerator = await IncrementalKernelGenerator
.newInstance(compilerOptions, entryPoint, watch: watchFn);
return (await incrementalKernelGenerator.computeDelta()).newProgram;

if (initialState != null) {
incrementalKernelGenerator.setState(initialState);
}

return await incrementalKernelGenerator.computeDelta();
}

test_acceptLastDelta() async {
Expand Down Expand Up @@ -94,7 +101,8 @@ void main() {}
''');

{
Program program = await getInitialState(cUri);
DeltaProgram delta = await getInitialState(cUri);
Program program = delta.newProgram;
incrementalKernelGenerator.acceptLastDelta();
_assertLibraryUris(program,
includes: [aUri, bUri, cUri, Uri.parse('dart:core')]);
Expand Down Expand Up @@ -172,7 +180,8 @@ b() {
Uri dUri = writeFile(dPath, 'd() {}');

{
Program program = await getInitialState(aUri);
DeltaProgram delta = await getInitialState(aUri);
Program program = delta.newProgram;
incrementalKernelGenerator.acceptLastDelta();
_assertLibraryUris(program,
includes: [aUri, bUri, cUri, dUri, Uri.parse('dart:core')]);
Expand Down Expand Up @@ -209,7 +218,8 @@ part 'b.dart';
part of lib;
''');

Program program = await getInitialState(aUri);
DeltaProgram delta = await getInitialState(aUri);
Program program = delta.newProgram;

// Sources for library and its part must be present.
expect(program.uriToSource.keys, contains(aUri.toString()));
Expand Down Expand Up @@ -239,7 +249,9 @@ var a = 1;
Future<String> b;
''');

Program program = await getInitialState(bUri, sdkOutlineUri: sdkOutlineUri);
DeltaProgram delta =
await getInitialState(bUri, sdkOutlineUri: sdkOutlineUri);
Program program = delta.newProgram;
incrementalKernelGenerator.acceptLastDelta();
_assertLibraryUris(program,
includes: [bUri], excludes: [Uri.parse('dart:core')]);
Expand Down Expand Up @@ -304,7 +316,8 @@ int getValue() {
String path = '/test/lib/test.dart';
Uri uri = writeFile(path, 'main() {}');

Program program = await getInitialState(uri, embedSourceText: false);
DeltaProgram delta = await getInitialState(uri, embedSourceText: false);
Program program = delta.newProgram;

// The Source object is present in the map, but is empty.
Source source = program.uriToSource[uri.toString()];
Expand All @@ -324,7 +337,8 @@ var b = a;

// Ensures that the `.packages` file can be discovered automatically
// from the entry point file.
Program program = await getInitialState(bUri, setPackages: false);
DeltaProgram delta = await getInitialState(bUri, setPackages: false);
Program program = delta.newProgram;
Library library = _getLibrary(program, bUri);
expect(_getLibraryText(library), r'''
library;
Expand All @@ -343,7 +357,8 @@ static field core::int b = a::a;

// The first delta includes the the library.
{
Program program = await getInitialState(uri);
DeltaProgram delta = await getInitialState(uri);
Program program = delta.newProgram;
_assertLibraryUris(program, includes: [uri]);
Library library = _getLibrary(program, uri);
expect(_getLibraryText(library), contains('core::int v = 1'));
Expand Down Expand Up @@ -371,7 +386,8 @@ static field core::int b = a::a;

// The first delta includes the the library.
{
Program program = await getInitialState(uri);
DeltaProgram delta = await getInitialState(uri);
Program program = delta.newProgram;
_assertLibraryUris(program, includes: [uri]);
Library library = _getLibrary(program, uri);
expect(_getLibraryText(library), contains('core::int v = 1'));
Expand All @@ -393,6 +409,44 @@ static field core::int b = a::a;
}
}

test_setState() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
Uri aUri = writeFile(aPath, 'var a = 1;');
Uri bUri = writeFile(bPath, r'''
var b = 1;
''');
Uri cUri = writeFile(cPath, r'''
import 'a.dart';
import 'b.dart';
var c1 = a;
var c2 = b;
''');

String initialState;
{
DeltaProgram delta = await getInitialState(cUri);
Program program = delta.newProgram;
incrementalKernelGenerator.acceptLastDelta();
_assertLibraryUris(program,
includes: [aUri, bUri, cUri, Uri.parse('dart:core')]);
initialState = delta.state;
}

// Update a.dart, don't notify the old generator - we throw it away.
writeFile(aPath, 'var a = 1.2');

// Create a new generator with the initial state.
var delta = await getInitialState(cUri, initialState: initialState);

// Only a.dart and c.dart are in the delta.
// The state of b.dart is the same as in the initial state.
_assertLibraryUris(delta.newProgram,
includes: [aUri, cUri], excludes: [bUri, Uri.parse('dart:core')]);
}

test_updateEntryPoint() async {
writeFile('/test/.packages', 'test:lib/');
String path = '/test/lib/test.dart';
Expand All @@ -414,7 +468,8 @@ static method main() → dynamic {

// Compute the initial state.
{
Program program = await getInitialState(uri);
DeltaProgram delta = await getInitialState(uri);
Program program = delta.newProgram;
incrementalKernelGenerator.acceptLastDelta();
Library library = _getLibrary(program, uri);
expect(_getLibraryText(library), initialText);
Expand Down

0 comments on commit ed69540

Please sign in to comment.