Version 2.16.0-43.0.dev
Merge commit '4c31413f1ac42e471a8ae0da31574ce7b83f6f4a' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 2346ae1..0b6145c 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -370,10 +370,10 @@
}
var errorCode = diagnostic.errorCode;
+ var codeName = errorCode.name;
try {
- var codeName = errorCode.name;
if (errorCode is LintCode) {
- var generators = FixProcessor.lintProducerMap[errorCode.name] ?? [];
+ var generators = FixProcessor.lintProducerMap[codeName] ?? [];
await bulkApply(generators, codeName);
} else {
var generators = FixProcessor.nonLintProducerMap[errorCode] ?? [];
@@ -391,9 +391,7 @@
}
} catch (e, s) {
throw CaughtException.withMessage(
- 'Exception generating fix for ${errorCode.name} in ${result.path}',
- e,
- s);
+ 'Exception generating fix for $codeName in ${result.path}', e, s);
}
}
@@ -405,7 +403,7 @@
await _applyProducer(context, producer);
var newHash = computeChangeHash();
if (newHash != oldHash) {
- changeMap.add(context.resolvedResult.path, code);
+ changeMap.add(context.resolvedResult.path, code.toLowerCase());
}
}
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index b899fbe..3c5f245 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -275,13 +275,8 @@
performGlobalTypeInference(closedWorldAndIndices.closedWorld);
var indices = closedWorldAndIndices.indices;
if (options.writeDataUri != null) {
- if (options.noClosedWorldInData) {
- serializationTask.serializeGlobalTypeInference(
- globalTypeInferenceResults, indices);
- } else {
- serializationTask
- .serializeGlobalTypeInferenceLegacy(globalTypeInferenceResults);
- }
+ serializationTask.serializeGlobalTypeInference(
+ globalTypeInferenceResults, indices);
return;
}
await generateJavaScriptCode(globalTypeInferenceResults,
@@ -301,12 +296,6 @@
closedWorldAndIndices);
await generateJavaScriptCode(globalTypeInferenceResults,
indices: closedWorldAndIndices.indices);
- } else if (options.readDataUri != null) {
- // TODO(joshualitt) delete and clean up after google3 roll
- var globalTypeInferenceResults =
- await serializationTask.deserializeGlobalTypeInferenceLegacy(
- environment, abstractValueStrategy);
- await generateJavaScriptCode(globalTypeInferenceResults);
} else {
KernelResult result = await kernelLoader.load();
reporter.log("Kernel load complete");
@@ -543,16 +532,6 @@
if (stopAfterClosedWorld || options.stopAfterProgramSplit) return;
GlobalTypeInferenceResults globalInferenceResults =
performGlobalTypeInference(closedWorld);
- if (options.writeDataUri != null) {
- // TODO(joshualitt) delete after google3 roll.
- if (options.noClosedWorldInData) {
- throw '"no-closed-world-in-data" requires serializing closed world.';
- } else {
- serializationTask
- .serializeGlobalTypeInferenceLegacy(globalInferenceResults);
- }
- return;
- }
if (options.testMode) {
globalInferenceResults =
globalTypeInferenceResultsTestMode(globalInferenceResults);
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 4c33c5a..156cd9d 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -548,7 +548,7 @@
setWriteModularAnalysis),
OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
- OptionHandler(Flags.noClosedWorldInData, passThrough),
+ OptionHandler(Flags.noClosedWorldInData, ignoreOption),
OptionHandler('${Flags.readClosedWorld}|${Flags.readClosedWorld}=.+',
setReadClosedWorld),
OptionHandler('${Flags.writeClosedWorld}|${Flags.writeClosedWorld}=.+',
@@ -825,9 +825,7 @@
if (readStrategy == ReadStrategy.fromCodegen) {
fail("Cannot read and write serialized codegen simultaneously.");
}
- // TODO(joshualitt) cleanup after google3 roll.
- if (readStrategy != ReadStrategy.fromData &&
- readStrategy != ReadStrategy.fromDataAndClosedWorld) {
+ if (readStrategy != ReadStrategy.fromDataAndClosedWorld) {
fail("Can only write serialized codegen from serialized data.");
}
if (codegenShards == null) {
@@ -855,10 +853,7 @@
options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
break;
case ReadStrategy.fromData:
- // TODO(joshualitt): fail after Google3 roll.
- // fail("Must read from closed world and data.");
- readDataUri ??= Uri.base.resolve('$scriptName.data');
- options.add('${Flags.readData}=${readDataUri}');
+ fail("Must read from closed world and data.");
break;
case ReadStrategy.fromDataAndClosedWorld:
readClosedWorldUri ??= Uri.base.resolve('$scriptName.world');
@@ -868,19 +863,6 @@
break;
case ReadStrategy.fromCodegen:
case ReadStrategy.fromCodegenAndData:
- // TODO(joshualitt): fall through to fail after google3 roll.
- readDataUri ??= Uri.base.resolve('$scriptName.data');
- options.add('${Flags.readData}=${readDataUri}');
- readCodegenUri ??= Uri.base.resolve('$scriptName.code');
- options.add('${Flags.readCodegen}=${readCodegenUri}');
- if (codegenShards == null) {
- fail("Cannot write serialized codegen without setting "
- "${Flags.codegenShards}.");
- } else if (codegenShards <= 0) {
- fail("${Flags.codegenShards} must be a positive integer.");
- }
- options.add('${Flags.codegenShards}=$codegenShards');
- break;
case ReadStrategy.fromCodegenAndClosedWorld:
fail("Must read from closed world, data, and codegen");
break;
@@ -941,13 +923,7 @@
summary = 'Data files $input and $dataInput ';
break;
case ReadStrategy.fromData:
- // TODO(joshualitt): fail after google3 roll.
- //fail("Must read from closed world and data.");
- inputName = 'bytes data';
- inputSize = inputProvider.dartCharactersRead;
- String dataInput =
- fe.relativizeUri(Uri.base, readDataUri, Platform.isWindows);
- summary = 'Data files $input and $dataInput ';
+ fail("Must read from closed world and data.");
break;
case ReadStrategy.fromDataAndClosedWorld:
inputName = 'bytes data';
@@ -960,16 +936,6 @@
break;
case ReadStrategy.fromCodegen:
case ReadStrategy.fromCodegenAndData:
- // TODO(joshualitt): Fall through to fail after google3 roll.
- inputName = 'bytes data';
- inputSize = inputProvider.dartCharactersRead;
- String dataInput =
- fe.relativizeUri(Uri.base, readDataUri, Platform.isWindows);
- String codeInput =
- fe.relativizeUri(Uri.base, readCodegenUri, Platform.isWindows);
- summary = 'Data files $input, $dataInput and '
- '${codeInput}[0-${codegenShards - 1}] ';
- break;
case ReadStrategy.fromCodegenAndClosedWorld:
fail("Must read from closed world, data, and codegen");
break;
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 27d75ae..e63f515 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -66,40 +66,6 @@
closedWorld.elementMap, closedWorld, globalLocalsMap, inferredData);
}
-void serializeGlobalTypeInferenceResultsToSinkLegacy(
- GlobalTypeInferenceResults results, DataSink sink) {
- JsClosedWorld closedWorld = results.closedWorld;
- GlobalLocalsMap globalLocalsMap = results.globalLocalsMap;
- InferredData inferredData = results.inferredData;
- closedWorld.writeToDataSink(sink);
- globalLocalsMap.writeToDataSink(sink);
- inferredData.writeToDataSink(sink);
- results.writeToDataSink(sink, closedWorld.elementMap);
- sink.close();
-}
-
-GlobalTypeInferenceResults
- deserializeGlobalTypeInferenceResultsFromSourceLegacy(
- CompilerOptions options,
- DiagnosticReporter reporter,
- Environment environment,
- AbstractValueStrategy abstractValueStrategy,
- ir.Component component,
- DataSource source) {
- JsClosedWorld newClosedWorld = JsClosedWorld.readFromDataSource(
- options, reporter, environment, abstractValueStrategy, component, source);
- GlobalLocalsMap newGlobalLocalsMap = GlobalLocalsMap.readFromDataSource(
- newClosedWorld.closureDataLookup.getEnclosingMember, source);
- InferredData newInferredData =
- InferredData.readFromDataSource(source, newClosedWorld);
- return GlobalTypeInferenceResults.readFromDataSource(
- source,
- newClosedWorld.elementMap,
- newClosedWorld,
- newGlobalLocalsMap,
- newInferredData);
-}
-
void serializeClosedWorldToSink(JsClosedWorld closedWorld, DataSink sink) {
closedWorld.writeToDataSink(sink);
sink.close();
@@ -321,39 +287,6 @@
});
}
- // TODO(joshualitt) get rid of legacy functions after Google3 roll.
- void serializeGlobalTypeInferenceLegacy(GlobalTypeInferenceResults results) {
- JsClosedWorld closedWorld = results.closedWorld;
- ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
- serializeComponent(component);
-
- measureSubtask('serialize data', () {
- _reporter.log('Writing data to ${_options.writeDataUri}');
- api.BinaryOutputSink dataOutput =
- _outputProvider.createBinarySink(_options.writeDataUri);
- DataSink sink = BinarySink(BinaryOutputSinkAdapter(dataOutput));
- serializeGlobalTypeInferenceResultsToSinkLegacy(results, sink);
- });
- }
-
- Future<GlobalTypeInferenceResults> deserializeGlobalTypeInferenceLegacy(
- Environment environment,
- AbstractValueStrategy abstractValueStrategy) async {
- ir.Component component = await deserializeComponentAndUpdateOptions();
-
- return await measureIoSubtask('deserialize data', () async {
- _reporter.log('Reading data from ${_options.readDataUri}');
- api.Input<List<int>> dataInput = await _provider
- .readFromUri(_options.readDataUri, inputKind: api.InputKind.binary);
- DataSource source =
- BinarySourceImpl(dataInput.data, stringInterner: _stringInterner);
- return deserializeGlobalTypeInferenceResultsFromSourceLegacy(_options,
- _reporter, environment, abstractValueStrategy, component, source);
- });
- }
-
- // TODO(joshualitt): Investigate whether closed world indices can be shared
- // with codegen.
void serializeCodegen(BackendStrategy backendStrategy,
CodegenResults codegenResults, DataSourceIndices indices) {
GlobalTypeInferenceResults globalTypeInferenceResults =
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 2ceac61..ab0b1a6 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -117,9 +117,24 @@
.isEmitted;
}
- HBoundsCheck insertBoundsCheck(HInstruction indexerNode, HInstruction array,
- HInstruction indexArgument, JClosedWorld closedWorld) {
+ HBoundsCheck insertBoundsCheck(
+ HInvokeDynamic indexerNode,
+ HInstruction array,
+ HInstruction indexArgument,
+ JClosedWorld closedWorld,
+ OptimizationTestLog log) {
final abstractValueDomain = closedWorld.abstractValueDomain;
+
+ if (abstractValueDomain.isNull(array.instructionType).isPotentiallyTrue) {
+ HNullCheck check = HNullCheck(
+ array, abstractValueDomain.excludeNull(array.instructionType))
+ ..selector = indexerNode.selector
+ ..sourceInformation = indexerNode.sourceInformation;
+ log?.registerNullCheck(indexerNode, check);
+ indexerNode.block.addBefore(indexerNode, check);
+ array = check;
+ }
+
HGetLength length = HGetLength(array, abstractValueDomain.positiveIntType,
isAssignable: abstractValueDomain
.isFixedLengthJsIndexable(array.instructionType)
@@ -212,7 +227,7 @@
HInstruction checkedIndex = index;
if (requiresBoundsCheck(instruction, closedWorld)) {
checkedIndex =
- insertBoundsCheck(instruction, receiver, index, closedWorld);
+ insertBoundsCheck(instruction, receiver, index, closedWorld, log);
}
HIndexAssign converted = HIndexAssign(
closedWorld.abstractValueDomain, receiver, checkedIndex, value);
@@ -291,7 +306,7 @@
HInstruction checkedIndex = index;
if (requiresBoundsCheck(instruction, closedWorld)) {
checkedIndex =
- insertBoundsCheck(instruction, receiver, index, closedWorld);
+ insertBoundsCheck(instruction, receiver, index, closedWorld, log);
}
HIndex converted = HIndex(receiver, checkedIndex, elementType);
log?.registerIndex(instruction, converted);
@@ -322,7 +337,7 @@
if (requiresBoundsCheck(instruction, closedWorld)) {
HConstant zeroIndex = graph.addConstantInt(0, closedWorld);
HBoundsCheck check =
- insertBoundsCheck(instruction, receiver, zeroIndex, closedWorld);
+ insertBoundsCheck(instruction, receiver, zeroIndex, closedWorld, log);
HInstruction minusOne = graph.addConstantInt(-1, closedWorld);
check.inputs.add(minusOne);
minusOne.usedBy.add(check);
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index d600d7e..0475798 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -606,8 +606,20 @@
.isDefinitelyTrue) {
resultType = _abstractValueDomain.uint32Type;
}
+ HInstruction checkedReceiver = actualReceiver;
+ if (actualReceiver.isNull(_abstractValueDomain).isPotentiallyTrue) {
+ // The receiver is potentially `null` so we insert a null receiver
+ // check. This will be folded into the length access later.
+ HNullCheck check = HNullCheck(actualReceiver,
+ _abstractValueDomain.excludeNull(actualReceiver.instructionType))
+ ..selector = node.selector
+ ..sourceInformation = node.sourceInformation;
+ _log?.registerNullCheck(node, check);
+ node.block.addBefore(node, check);
+ checkedReceiver = check;
+ }
HGetLength result =
- HGetLength(actualReceiver, resultType, isAssignable: !isFixed);
+ HGetLength(checkedReceiver, resultType, isAssignable: !isFixed);
return result;
} else if (actualReceiver.isConstantMap()) {
HConstant constantInput = actualReceiver;
@@ -2607,12 +2619,14 @@
if (branch is HIf) {
if (branch.thenBlock.isLive == branch.elseBlock.isLive) return;
assert(branch.condition.isConstant());
- HBasicBlock target =
+ HBasicBlock liveSuccessor =
branch.thenBlock.isLive ? branch.thenBlock : branch.elseBlock;
- HInstruction instruction = target.first;
- while (!instruction.isControlFlow()) {
- HInstruction next = instruction.next;
+ HInstruction instruction = liveSuccessor.first;
+ // Move instructions up until the final control flow instruction or pinned
+ // HTypeKnown.
+ while (instruction.next != null) {
if (instruction is HTypeKnown && instruction.isPinned) break;
+ HInstruction next = instruction.next;
// It might be worth re-running GVN optimizations if we hoisted a
// GVN-able instructions from [target] into [block].
newGvnCandidates = newGvnCandidates || instruction.useGvn();
diff --git a/pkg/compiler/test/end_to_end/command_line_test.dart b/pkg/compiler/test/end_to_end/command_line_test.dart
index ec0a78a..f3ecc57 100644
--- a/pkg/compiler/test/end_to_end/command_line_test.dart
+++ b/pkg/compiler/test/end_to_end/command_line_test.dart
@@ -91,14 +91,27 @@
'--out=foo.dill'
], out: 'foo.dill', readClosedWorld: 'foo.world', writeData: 'foo.data');
- await test([Flags.readData, 'foo.dill'],
- out: 'out.js', readData: 'foo.dill.data');
- await test([Flags.readData, 'foo.dill', '--out=foo.js'],
- out: 'foo.js', readData: 'foo.dill.data');
- await test(['${Flags.readData}=out.data', 'foo.dill'],
- out: 'out.js', readData: 'out.data');
- await test(['${Flags.readData}=out.data', 'foo.dill', '--out=foo.js'],
- out: 'foo.js', readData: 'out.data');
+ await test([Flags.readData, 'foo.dill'], exitCode: 1);
+ await test([Flags.readClosedWorld, Flags.readData, 'foo.dill'],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data');
+ await test(
+ [Flags.readClosedWorld, Flags.readData, 'foo.dill', '--out=foo.js'],
+ out: 'foo.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data');
+ await test([
+ '${Flags.readClosedWorld}=out.world',
+ '${Flags.readData}=out.data',
+ 'foo.dill'
+ ], out: 'out.js', readClosedWorld: 'out.world', readData: 'out.data');
+ await test([
+ '${Flags.readClosedWorld}=out.world',
+ '${Flags.readData}=out.data',
+ 'foo.dill',
+ '--out=foo.js'
+ ], out: 'foo.js', readClosedWorld: 'out.world', readData: 'out.data');
await test([
Flags.writeCodegen,
@@ -107,6 +120,7 @@
'${Flags.codegenShards}=2'
], exitCode: 1);
await test([
+ Flags.readClosedWorld,
Flags.readData,
Flags.writeCodegen,
'foo.dill',
@@ -114,6 +128,7 @@
'${Flags.codegenShards}=2'
],
out: 'out',
+ readClosedWorld: 'foo.dill.world',
readData: 'foo.dill.data',
writeCodegen: 'out.code',
codegenShard: 0,
@@ -121,16 +136,19 @@
await test([
Flags.writeCodegen,
Flags.readData,
+ Flags.readClosedWorld,
'foo.dill',
'${Flags.codegenShard}=1',
'${Flags.codegenShards}=2'
],
out: 'out',
+ readClosedWorld: 'foo.dill.world',
readData: 'foo.dill.data',
writeCodegen: 'out.code',
codegenShard: 1,
codegenShards: 2);
await test([
+ '${Flags.readClosedWorld}=foo.world',
'${Flags.readData}=foo.data',
'${Flags.writeCodegen}=foo.code',
'foo.dill',
@@ -138,6 +156,7 @@
'${Flags.codegenShards}=3'
],
out: 'out',
+ readClosedWorld: 'foo.world',
readData: 'foo.data',
writeCodegen: 'foo.code',
codegenShard: 0,
@@ -145,12 +164,14 @@
await test([
'${Flags.readData}=foo.data',
'${Flags.writeCodegen}',
+ '${Flags.readClosedWorld}=foo.world',
'foo.dill',
'--out=foo.js',
'${Flags.codegenShard}=0',
'${Flags.codegenShards}=2'
],
out: 'foo.js',
+ readClosedWorld: 'foo.world',
readData: 'foo.data',
writeCodegen: 'foo.js.code',
codegenShard: 0,
@@ -203,28 +224,126 @@
'${Flags.codegenShards}=2'
], exitCode: 1);
- await test([Flags.readCodegen, 'foo.dill', '${Flags.codegenShards}=2'],
- out: 'out.js',
- readData: 'foo.dill.data',
- readCodegen: 'foo.dill.code',
- codegenShards: 2);
+ // These three flags should parse in any order, but all are required.
await test([
- '${Flags.readCodegen}=foo.code',
- 'foo.dill',
- '${Flags.codegenShards}=3'
- ],
- out: 'out.js',
- readData: 'foo.dill.data',
- readCodegen: 'foo.code',
- codegenShards: 3);
-
- await test([
+ Flags.readClosedWorld,
Flags.readData,
Flags.readCodegen,
'foo.dill',
'${Flags.codegenShards}=2'
],
out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ Flags.readClosedWorld,
+ Flags.readCodegen,
+ Flags.readData,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ Flags.readCodegen,
+ Flags.readClosedWorld,
+ Flags.readData,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ Flags.readCodegen,
+ Flags.readData,
+ Flags.readClosedWorld,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ Flags.readData,
+ Flags.readCodegen,
+ Flags.readClosedWorld,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ Flags.readData,
+ Flags.readClosedWorld,
+ Flags.readCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ Flags.readClosedWorld,
+ Flags.readCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ], exitCode: 1);
+ await test([
+ Flags.readData,
+ Flags.readCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ], exitCode: 1);
+
+ await test([
+ Flags.readData,
+ Flags.readClosedWorld,
+ Flags.readCodegen,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
+ readData: 'foo.dill.data',
+ readCodegen: 'foo.dill.code',
+ codegenShards: 2);
+ await test([
+ '${Flags.readCodegen}=foo.code',
+ 'foo.dill',
+ '${Flags.codegenShards}=3',
+ Flags.readData,
+ Flags.readClosedWorld,
+ ],
+ out: 'out.js',
+ readData: 'foo.dill.data',
+ readClosedWorld: 'foo.dill.world',
+ readCodegen: 'foo.code',
+ codegenShards: 3);
+
+ await test([
+ Flags.readData,
+ Flags.readCodegen,
+ Flags.readClosedWorld,
+ 'foo.dill',
+ '${Flags.codegenShards}=2'
+ ],
+ out: 'out.js',
+ readClosedWorld: 'foo.dill.world',
readData: 'foo.dill.data',
readCodegen: 'foo.dill.code',
codegenShards: 2);
@@ -233,9 +352,11 @@
'${Flags.readCodegen}=foo.code',
'foo.dill',
'${Flags.codegenShards}=3',
+ '${Flags.readClosedWorld}=foo.world',
'-v'
],
out: 'out.js',
+ readClosedWorld: 'foo.world',
readData: 'foo.data',
readCodegen: 'foo.code',
codegenShards: 3);
diff --git a/pkg/compiler/test/model/cfe_annotations_test.dart b/pkg/compiler/test/model/cfe_annotations_test.dart
index c9bd48e..79220177 100644
--- a/pkg/compiler/test/model/cfe_annotations_test.dart
+++ b/pkg/compiler/test/model/cfe_annotations_test.dart
@@ -434,6 +434,7 @@
implicitJsInteropMember:
nativeData.isJsInteropClass(classEntity),
implicitNativeMember: member is! ir.Constructor &&
+ !_isConstructorTearOff(member) &&
nativeData.isNativeClass(classEntity) &&
!nativeData.isJsInteropClass(classEntity));
}
@@ -465,3 +466,13 @@
await runTest(useIr: true);
});
}
+
+bool _isConstructorTearOff(ir.Member member) {
+ if (member is ir.Procedure) {
+ if (member.kind == ir.ProcedureKind.Method &&
+ member.name.text.endsWith('#tearOff')) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 1593208..fa6b531 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -385,7 +385,6 @@
if (useModularAnalysis)
'${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
'${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
- Flags.noClosedWorldInData,
'--out=${toUri(module, globalUpdatedDillId)}',
];
var result =
@@ -433,8 +432,6 @@
for (String flag in flags) '--enable-experiment=$flag',
'${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
'${Flags.writeData}=${toUri(module, globalDataId)}',
- // TODO(joshualitt): delete this flag after google3 roll
- '${Flags.noClosedWorldInData}',
];
var result =
await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
@@ -555,153 +552,6 @@
}
}
-// TODO(joshualitt): delete after google3 roll.
-class LegacyGlobalAnalysisStep implements IOModularStep {
- @override
- List<DataId> get resultData => const [globalDataId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [globalUpdatedDillId];
-
- @override
- List<DataId> get moduleDataNeeded =>
- const [closedWorldId, globalUpdatedDillId];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("\nstep: dart2js global analysis on $module");
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- // TODO(sigmund): remove this dependency on libraries.json
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${toUri(module, globalUpdatedDillId)}',
- for (String flag in flags) '--enable-experiment=$flag',
- '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
- '${Flags.writeData}=${toUri(module, globalDataId)}',
- ];
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
-
- _checkExitCode(result, this, module);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose)
- print("\ncached step: dart2js global analysis on $module");
- }
-}
-
-// Step that invokes the dart2js code generation on the main module given the
-// results of the global analysis step and produces one shard of the codegen
-// output.
-// Note: Legacy.
-class LegacyDart2jsCodegenStep implements IOModularStep {
- final ShardDataId codeId;
-
- LegacyDart2jsCodegenStep(this.codeId);
-
- @override
- List<DataId> get resultData => [codeId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [];
-
- @override
- List<DataId> get moduleDataNeeded =>
- const [globalUpdatedDillId, globalDataId];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("\nstep: dart2js backend on $module");
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${toUri(module, globalUpdatedDillId)}',
- for (String flag in flags) '--enable-experiment=$flag',
- '${Flags.readData}=${toUri(module, globalDataId)}',
- '${Flags.writeCodegen}=${toUri(module, codeId.dataId)}',
- '${Flags.codegenShard}=${codeId.shard}',
- '${Flags.codegenShards}=${codeId.dataId.shards}',
- ];
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
-
- _checkExitCode(result, this, module);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose) print("cached step: dart2js backend on $module");
- }
-}
-
-// Step that invokes the dart2js codegen enqueuer and emitter on the main module
-// given the results of the global analysis step and codegen shards.
-// Note: Legacy.
-class LegacyDart2jsEmissionStep implements IOModularStep {
- @override
- List<DataId> get resultData => const [jsId];
-
- @override
- bool get needsSources => false;
-
- @override
- List<DataId> get dependencyDataNeeded => const [];
-
- @override
- List<DataId> get moduleDataNeeded =>
- const [globalUpdatedDillId, globalDataId, codeId0, codeId1];
-
- @override
- bool get onlyOnMain => true;
-
- @override
- Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
- List<String> flags) async {
- if (_options.verbose) print("step: dart2js backend on $module");
- List<String> args = [
- '--packages=${sdkRoot.toFilePath()}/.packages',
- _dart2jsScript,
- if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
- '${Flags.entryUri}=$fakeRoot${module.mainSource}',
- '${toUri(module, globalUpdatedDillId)}',
- for (String flag in flags) '${Flags.enableLanguageExperiments}=$flag',
- '${Flags.readData}=${toUri(module, globalDataId)}',
- '${Flags.readCodegen}=${toUri(module, codeId)}',
- '${Flags.codegenShards}=${codeId.shards}',
- '--out=${toUri(module, jsId)}',
- ];
- var result =
- await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
-
- _checkExitCode(result, this, module);
- }
-
- @override
- void notifyCached(Module module) {
- if (_options.verbose) print("\ncached step: dart2js backend on $module");
- }
-}
-
/// Step that runs the output of dart2js in d8 and saves the output.
class RunD8 implements IOModularStep {
@override
diff --git a/pkg/dartdev/lib/src/analysis_server.dart b/pkg/dartdev/lib/src/analysis_server.dart
index 580619b..1666afc 100644
--- a/pkg/dartdev/lib/src/analysis_server.dart
+++ b/pkg/dartdev/lib/src/analysis_server.dart
@@ -8,7 +8,11 @@
import 'package:analysis_server/src/server/driver.dart' show Driver;
import 'package:analysis_server_client/protocol.dart'
- show EditBulkFixesResult, ResponseDecoder;
+ show
+ AddContentOverlay,
+ AnalysisUpdateContentParams,
+ EditBulkFixesResult,
+ ResponseDecoder;
import 'package:args/args.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
@@ -167,6 +171,12 @@
});
}
+ /// Send an `analysis.updateContent` request with the given [files].
+ Future<void> updateContent(Map<String, AddContentOverlay> files) async {
+ await _sendCommand('analysis.updateContent',
+ params: AnalysisUpdateContentParams(files).toJson());
+ }
+
Future<Map<String, dynamic>> _sendCommand(String method,
{Map<String, dynamic> params}) {
final String id = (++_id).toString();
diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart
index c81e467..83c2faa 100644
--- a/pkg/dartdev/lib/src/commands/fix.dart
+++ b/pkg/dartdev/lib/src/commands/fix.dart
@@ -26,6 +26,12 @@
To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed changes for a project, or ['dart fix --apply'] to apply the changes.''';
+ /// The maximum number of times that fixes will be requested from the server.
+ static const maxPasses = 4;
+
+ /// A map from the absolute path of a file to the updated content of the file.
+ final Map<String, String> fileContentCache = {};
+
FixCommand({bool verbose = false}) : super(cmdName, cmdDescription, verbose) {
argParser.addFlag('dry-run',
abbr: 'n',
@@ -86,7 +92,7 @@
var modeText = dryRun ? ' (dry run)' : '';
final projectName = path.basename(dirPath);
- var progress = log.progress(
+ var computeFixesProgress = log.progress(
'Computing fixes in ${log.ansi.emphasized(projectName)}$modeText');
var server = AnalysisServer(
@@ -99,53 +105,60 @@
await server.start();
- EditBulkFixesResult fixes;
server.onExit.then((int exitCode) {
- if (fixes == null && exitCode != 0) {
- progress?.cancel();
+ if (computeFixesProgress != null && exitCode != 0) {
+ computeFixesProgress?.cancel();
+ computeFixesProgress = null;
io.exitCode = exitCode;
}
});
- fixes = await server.requestBulkFixes(dirPath, inTestMode);
- final List<SourceFileEdit> edits = fixes.edits;
+ Future<Map<String, BulkFix>> _applyAllEdits() async {
+ var detailsMap = <String, BulkFix>{};
+ List<SourceFileEdit> edits;
+ var pass = 0;
+ do {
+ var fixes = await server.requestBulkFixes(dirPath, inTestMode);
+ _mergeDetails(detailsMap, fixes.details);
+ edits = fixes.edits;
+ _applyEdits(server, edits);
+ pass++;
+ // TODO(brianwilkerson) Be more intelligent about detecting infinite
+ // loops so that we can increase [maxPasses].
+ } while (pass < maxPasses && edits.isNotEmpty);
+ return detailsMap;
+ }
+ var detailsMap = await _applyAllEdits();
await server.shutdown();
- progress.finish(showTiming: true);
+ if (computeFixesProgress != null) {
+ computeFixesProgress.finish(showTiming: true);
+ computeFixesProgress = null;
+ }
if (inTestMode) {
- var result = _compareFixesInDirectory(dir, edits);
+ var result = _compareFixesInDirectory(dir);
log.stdout('Passed: ${result.passCount}, Failed: ${result.failCount}');
return result.failCount > 0 ? 1 : 0;
- } else if (edits.isEmpty) {
+ } else if (detailsMap.isEmpty) {
log.stdout('Nothing to fix!');
} else {
- var details = fixes.details;
- details.sort((f1, f2) => path
- .relative(f1.path, from: dirPath)
- .compareTo(path.relative(f2.path, from: dirPath)));
-
- var fileCount = 0;
- var fixCount = 0;
-
- for (var d in details) {
- ++fileCount;
- for (var f in d.fixes) {
- fixCount += f.occurrences;
- }
- }
+ var fileCount = detailsMap.length;
+ var fixCount = detailsMap.values
+ .expand((detail) => detail.fixes)
+ .fold(0, (previousValue, fixes) => previousValue + fixes.occurrences);
if (dryRun) {
log.stdout('');
log.stdout('${_format(fixCount)} proposed ${_pluralFix(fixCount)} '
'in ${_format(fileCount)} ${pluralize("file", fileCount)}.');
- _printDetails(details, dir);
+ _printDetails(detailsMap, dir);
} else {
- progress = log.progress('Applying fixes');
- _applyFixes(edits);
- progress.finish(showTiming: true);
- _printDetails(details, dir);
+ var applyFixesProgress = log.progress('Applying fixes');
+ _writeFiles();
+ applyFixesProgress.finish(showTiming: true);
+ _printDetails(detailsMap, dir);
log.stdout('${_format(fixCount)} ${_pluralFix(fixCount)} made in '
'${_format(fileCount)} ${pluralize("file", fileCount)}.');
}
@@ -154,20 +167,24 @@
return 0;
}
- void _applyFixes(List<SourceFileEdit> edits) {
+ void _applyEdits(AnalysisServer server, List<SourceFileEdit> edits) {
+ var overlays = <String, AddContentOverlay>{};
for (var edit in edits) {
- var fileName = edit.file;
- var file = io.File(fileName);
- var code = file.existsSync() ? file.readAsStringSync() : '';
- code = SourceEdit.applySequence(code, edit.edits);
- file.writeAsStringSync(code);
+ var filePath = edit.file;
+ var content = fileContentCache.putIfAbsent(filePath, () {
+ var file = io.File(filePath);
+ return file.existsSync() ? file.readAsStringSync() : '';
+ });
+ var newContent = SourceEdit.applySequence(content, edit.edits);
+ fileContentCache[filePath] = newContent;
+ overlays[filePath] = AddContentOverlay(newContent);
}
+ server.updateContent(overlays);
}
/// Return `true` if any of the fixes fail to create the same content as is
/// found in the golden file.
- _TestResult _compareFixesInDirectory(
- io.Directory directory, List<SourceFileEdit> edits) {
+ _TestResult _compareFixesInDirectory(io.Directory directory) {
var result = _TestResult();
//
// Gather the files of interest in this directory and process
@@ -177,7 +194,7 @@
var expectFileMap = <String, io.File>{};
for (var child in directory.listSync()) {
if (child is io.Directory) {
- var childResult = _compareFixesInDirectory(child, edits);
+ var childResult = _compareFixesInDirectory(child);
result.passCount += childResult.passCount;
result.failCount += childResult.failCount;
} else if (child is io.File) {
@@ -189,10 +206,6 @@
}
}
}
- var editMap = <String, SourceFileEdit>{};
- for (var edit in edits) {
- editMap[edit.file] = edit;
- }
for (var originalFile in dartFiles) {
var filePath = originalFile.path;
var baseName = path.basename(filePath);
@@ -205,19 +218,17 @@
'No corresponding expect file for the Dart file at "$filePath".');
continue;
}
- var edit = editMap[filePath];
try {
- var originalCode = originalFile.readAsStringSync();
var expectedCode = expectFile.readAsStringSync();
- var actualCode = edit == null
- ? originalCode
- : SourceEdit.applySequence(originalCode, edit.edits);
+ var actualCode =
+ fileContentCache[filePath] ?? originalFile.readAsStringSync();
// Use a whitespace insensitive comparison.
if (_compressWhitespace(actualCode) !=
_compressWhitespace(expectedCode)) {
result.failCount++;
+ // TODO(brianwilkerson) Do a better job of displaying the differences.
+ // It's very hard to see the diff with large files.
_reportFailure(filePath, actualCode, expectedCode);
- _printEdits(edits);
} else {
result.passCount++;
}
@@ -243,15 +254,54 @@
String _compressWhitespace(String code) =>
code.replaceAll(RegExp(r'\s+'), ' ');
+ /// Merge the fixes from the current round's [details] into the [detailsMap].
+ void _mergeDetails(Map<String, BulkFix> detailsMap, List<BulkFix> details) {
+ for (var detail in details) {
+ var previousDetail = detailsMap[detail.path];
+ if (previousDetail != null) {
+ _mergeFixCounts(previousDetail.fixes, detail.fixes);
+ } else {
+ detailsMap[detail.path] = detail;
+ }
+ }
+ }
+
+ void _mergeFixCounts(
+ List<BulkFixDetail> oldFixes, List<BulkFixDetail> newFixes) {
+ var originalOldLength = oldFixes.length;
+ newFixLoop:
+ for (var newFix in newFixes) {
+ var newCode = newFix.code;
+ // Iterate over the original content of the list, not any of the newly
+ // added fixes, because the newly added fixes can't be a match.
+ for (var i = 0; i < originalOldLength; i++) {
+ var oldFix = oldFixes[i];
+ if (oldFix.code == newCode) {
+ oldFix.occurrences += newFix.occurrences;
+ continue newFixLoop;
+ }
+ }
+ oldFixes.add(newFix);
+ }
+ }
+
String _pluralFix(int count) => count == 1 ? 'fix' : 'fixes';
- void _printDetails(List<BulkFix> details, io.Directory workingDir) {
+ void _printDetails(Map<String, BulkFix> detailsMap, io.Directory workingDir) {
+ String relative(String absolutePath) {
+ return path.relative(absolutePath, from: workingDir.path);
+ }
+
log.stdout('');
final bullet = log.ansi.bullet;
- for (var detail in details) {
- log.stdout(path.relative(detail.path, from: workingDir.path));
+ var modifiedFilePaths = detailsMap.keys.toList();
+ modifiedFilePaths
+ .sort((first, second) => relative(first).compareTo(relative(second)));
+ for (var filePath in modifiedFilePaths) {
+ var detail = detailsMap[filePath];
+ log.stdout(relative(detail.path));
final fixes = detail.fixes.toList();
fixes.sort((a, b) => a.code.compareTo(b.code));
for (var fix in fixes) {
@@ -262,16 +312,6 @@
}
}
- void _printEdits(List<SourceFileEdit> edits) {
- log.stdout('Edits returned from server:');
- for (var fileEdit in edits) {
- log.stdout(' ${fileEdit.file}');
- for (var edit in fileEdit.edits) {
- log.stdout(" ${edit.offset} - ${edit.end}, '${edit.replacement}'");
- }
- }
- }
-
/// Report that the [actualCode] produced by applying fixes to the content of
/// [filePath] did not match the [expectedCode].
void _reportFailure(String filePath, String actualCode, String expectedCode) {
@@ -283,6 +323,14 @@
log.stdout(actualCode);
}
+ /// Write the modified contents of files in the [fileContentCache] to disk.
+ void _writeFiles() {
+ for (var entry in fileContentCache.entries) {
+ var file = io.File(entry.key);
+ file.writeAsStringSync(entry.value);
+ }
+ }
+
static String _format(int value) => _numberFormat.format(value);
}
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 036eb7d..1f0dd94 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -195,6 +195,32 @@
]));
});
+ test('--apply (contradictory lints do not loop infinitely)', () async {
+ p = project(
+ mainSrc: '''
+var x = "";
+''',
+ analysisOptions: '''
+linter:
+ rules:
+ - prefer_double_quotes
+ - prefer_single_quotes
+''',
+ );
+ var result = await runFix(['--apply', '.'], workingDir: p.dirPath);
+ expect(result.exitCode, 0);
+ expect(result.stderr, isEmpty);
+ expect(
+ result.stdout,
+ stringContainsInOrder([
+ 'Applying fixes...',
+ 'lib${Platform.pathSeparator}main.dart',
+ ' prefer_double_quotes $bullet 2 fixes',
+ ' prefer_single_quotes $bullet 2 fixes',
+ '4 fixes made in 1 file.',
+ ]));
+ });
+
test('--apply (excludes)', () async {
p = project(
mainSrc: '''
@@ -233,6 +259,33 @@
expect(result.stdout, contains('Nothing to fix!'));
});
+ test('--apply (unused imports require a second pass)', () async {
+ p = project(
+ mainSrc: '''
+import 'dart:math';
+
+var x = "";
+''',
+ analysisOptions: '''
+linter:
+ rules:
+ - prefer_single_quotes
+''',
+ );
+ var result = await runFix(['--apply', '.'], workingDir: p.dirPath);
+ expect(result.exitCode, 0);
+ expect(result.stderr, isEmpty);
+ expect(
+ result.stdout,
+ stringContainsInOrder([
+ 'Applying fixes...',
+ 'lib${Platform.pathSeparator}main.dart',
+ ' prefer_single_quotes $bullet 1 fix',
+ ' unused_import $bullet 1 fix',
+ '2 fixes made in 1 file.',
+ ]));
+ });
+
group('compare-to-golden', () {
test('applied fixes do not match expected', () async {
p = project(
diff --git a/tools/VERSION b/tools/VERSION
index 188507e..9ffa67f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 42
+PRERELEASE 43
PRERELEASE_PATCH 0
\ No newline at end of file