Add FeaturesDataInterpreter
Change-Id: I40a7baca235c2e729629f4330d8d51f148264ece
Reviewed-on: https://dart-review.googlesource.com/c/88963
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 1336828..b5fc867 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -47,9 +47,9 @@
var constraint = receiverConstraint;
if (constraint is StrongModeConstraint) {
if (constraint.isThis) {
- sb.write('<');
+ sb.write('this:');
} else if (constraint.isExact) {
- sb.write('=');
+ sb.write('exact:');
}
sb.write(constraint.cls.name);
} else {
diff --git a/pkg/compiler/lib/src/util/features.dart b/pkg/compiler/lib/src/util/features.dart
index 57aeba2..5263b40 100644
--- a/pkg/compiler/lib/src/util/features.dart
+++ b/pkg/compiler/lib/src/util/features.dart
@@ -25,9 +25,13 @@
_features[key] = value;
}
- String operator [](String key) => _features[key];
+ Object operator [](String key) => _features[key];
- String remove(String key) => _features.remove(key);
+ Object remove(String key) => _features.remove(key);
+
+ bool get isEmpty => _features.isEmpty;
+
+ bool get isNotEmpty => _features.isNotEmpty;
void forEach(void Function(String, Object) f) {
_features.forEach(f);
diff --git a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
index f9b8ebf..e7becbd 100644
--- a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
+++ b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
@@ -11,6 +11,7 @@
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/util/features.dart';
import 'package:expect/expect.dart';
import 'package:sourcemap_testing/src/annotated_code_helper.dart';
@@ -687,6 +688,87 @@
}
}
+class FeaturesDataInterpreter implements DataInterpreter<Features> {
+ const FeaturesDataInterpreter();
+
+ @override
+ String isAsExpected(Features actualFeatures, String expectedData) {
+ if (expectedData == '*') {
+ return null;
+ } else if (expectedData == '') {
+ return actualFeatures.isNotEmpty ? "Expected empty data." : null;
+ } else {
+ List<String> errorsFound = [];
+ Features expectedFeatures = Features.fromText(expectedData);
+ expectedFeatures.forEach((String key, Object expectedValue) {
+ Object actualValue = actualFeatures[key] ?? '';
+ if (expectedValue == '') {
+ if (actualValue != '') {
+ errorsFound.add('Non-empty data found for $key');
+ }
+ } else if (expectedValue == '*') {
+ return;
+ } else if (expectedValue is List) {
+ if (actualValue is List) {
+ List actualList = actualValue.toList();
+ for (Object expectedObject in expectedValue) {
+ String expectedText = '$expectedObject';
+ bool matchFound = false;
+ if (expectedText.endsWith('*')) {
+ // Wildcard matcher.
+ String prefix =
+ expectedText.substring(0, expectedText.indexOf('*'));
+ List matches = [];
+ for (Object actualObject in actualList) {
+ if ('$actualObject'.startsWith(prefix)) {
+ matches.add(actualObject);
+ matchFound = true;
+ }
+ }
+ for (Object match in matches) {
+ actualList.remove(match);
+ }
+ } else {
+ for (Object actualObject in actualList) {
+ if (expectedText == '$actualObject') {
+ actualList.remove(actualObject);
+ matchFound = true;
+ break;
+ }
+ }
+ }
+ if (!matchFound) {
+ errorsFound.add("No match found for $key=[$expectedText]");
+ }
+ }
+ if (actualList.isNotEmpty) {
+ errorsFound
+ .add("Extra data found $key=[${actualList.join(',')}]");
+ }
+ } else {
+ errorsFound.add("List data expected for $key: "
+ "expected '$expectedValue', found '${actualValue}'");
+ }
+ } else if (expectedValue != actualValue) {
+ errorsFound.add(
+ "Mismatch for $key: expected '$expectedValue', found '${actualValue}");
+ }
+ });
+ return errorsFound.isNotEmpty ? errorsFound.join(', ') : null;
+ }
+ }
+
+ @override
+ String getText(Features actualData) {
+ return actualData.getText();
+ }
+
+ @override
+ bool isEmpty(Features actualData) {
+ return actualData == null || actualData.isEmpty;
+ }
+}
+
/// Checks [compiledData] against the expected data in [expectedMap] derived
/// from [code].
Future<bool> checkCode<T>(
@@ -715,8 +797,9 @@
reportError(
data.compiler.reporter,
actualData.sourceSpan,
- 'EXTRA $mode DATA for ${id.descriptor} = '
- '${colorizeActual('${IdValue.idToString(id, actualText)}')} for ${actualData.objectText}. '
+ 'EXTRA $mode DATA for ${id.descriptor}:\n '
+ 'object : ${actualData.objectText}\n '
+ 'actual : ${colorizeActual('${IdValue.idToString(id, actualText)}')}\n '
'Data was expected for these ids: ${expectedMap.keys}');
if (filterActualData == null || filterActualData(null, actualData)) {
hasLocalFailure = true;
@@ -730,7 +813,7 @@
reportError(
data.compiler.reporter,
actualData.sourceSpan,
- 'UNEXPECTED $mode DATA for ${id.descriptor}: \n '
+ 'UNEXPECTED $mode DATA for ${id.descriptor}:\n '
'detail : ${colorizeMessage(unexpectedMessage)}\n '
'object : ${actualData.objectText}\n '
'expected: ${colorizeExpected('$expected')}\n '
diff --git a/tests/compiler/dart2js/impact/data/runtime_type.dart b/tests/compiler/dart2js/impact/data/runtime_type.dart
index 32fa0a7..b287715 100644
--- a/tests/compiler/dart2js/impact/data/runtime_type.dart
+++ b/tests/compiler/dart2js/impact/data/runtime_type.dart
@@ -5,7 +5,7 @@
/*element: Class1a.:static=[Object.(0)]*/
class Class1a<T> {
/*element: Class1a.==:
- dynamic=[<Class1a.runtimeType,Object.runtimeType,Type.==],
+ dynamic=[this:Class1a.runtimeType,Object.runtimeType,Type.==],
runtimeType=[equals:Class1a<Class1a.T>/dynamic]
*/
bool operator ==(other) {
@@ -16,7 +16,7 @@
/*element: Class1b.:static=[Class1a.(0)]*/
class Class1b<T> extends Class1a<T> {
/*element: Class1b.==:
- dynamic=[<Class1b.runtimeType,Object.runtimeType,Type.==],
+ dynamic=[this:Class1b.runtimeType,Object.runtimeType,Type.==],
runtimeType=[equals:dynamic/Class1b<Class1b.T>]
*/
bool operator ==(other) {
@@ -27,7 +27,7 @@
/*element: Class1c.:static=[Object.(0)]*/
class Class1c<T> implements Class1a<T> {
/*element: Class1c.==:
- dynamic=[<Class1c.runtimeType,Object.==,Object.runtimeType,Type.==],
+ dynamic=[this:Class1c.runtimeType,Object.==,Object.runtimeType,Type.==],
runtimeType=[equals:Class1c<Class1c.T>/dynamic],
type=[inst:JSNull]
*/
@@ -39,7 +39,7 @@
/*element: Class1d.:static=[Object.(0)]*/
class Class1d<T> implements Class1a<T> {
/*element: Class1d.==:
- dynamic=[<Class1d.runtimeType,Object.==,Object.runtimeType,Type.==],
+ dynamic=[this:Class1d.runtimeType,Object.==,Object.runtimeType,Type.==],
runtimeType=[equals:dynamic/Class1d<Class1d.T>],
type=[inst:JSNull]
*/
diff --git a/tests/compiler/dart2js/impact/data/this.dart b/tests/compiler/dart2js/impact/data/this.dart
index 2579dda..73a03d1 100644
--- a/tests/compiler/dart2js/impact/data/this.dart
+++ b/tests/compiler/dart2js/impact/data/this.dart
@@ -10,12 +10,12 @@
/*element: Class.field2:type=[inst:JSNull]*/
var field2;
- /*element: Class.method1:dynamic=[<Class.method2(0)]*/
+ /*element: Class.method1:dynamic=[this:Class.method2(0)]*/
method1() {
method2();
}
- /*element: Class.method2:dynamic=[<Class.field1=,<Class.field2]*/
+ /*element: Class.method2:dynamic=[this:Class.field1=,this:Class.field2]*/
method2() {
field1 = field2;
}
@@ -31,7 +31,7 @@
/*element: Subclass.method1:*/
method1() {}
- /*element: Subclass.method2:dynamic=[<Subclass.method3(0)]*/
+ /*element: Subclass.method2:dynamic=[this:Subclass.method3(0)]*/
method2() {
method3();
}
diff --git a/tests/compiler/dart2js/impact/impact_test.dart b/tests/compiler/dart2js/impact/impact_test.dart
index d1cc0b1b..e59b077 100644
--- a/tests/compiler/dart2js/impact/impact_test.dart
+++ b/tests/compiler/dart2js/impact/impact_test.dart
@@ -33,12 +33,12 @@
static const String runtimeTypeUse = 'runtimeType';
}
-class ImpactDataComputer extends DataComputer<String> {
+class ImpactDataComputer extends DataComputer<Features> {
const ImpactDataComputer();
@override
void computeMemberData(Compiler compiler, MemberEntity member,
- Map<Id, ActualData<String>> actualMap,
+ Map<Id, ActualData<Features>> actualMap,
{bool verbose: false}) {
KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
WorldImpact impact = compiler.impactCache[member];
@@ -72,10 +72,11 @@
}
}
Id id = computeEntityId(node);
- actualMap[id] = new ActualData<String>(
- id, features.getText(), computeSourceSpanFromTreeNode(node), member);
+ actualMap[id] = new ActualData<Features>(
+ id, features, computeSourceSpanFromTreeNode(node), member);
}
@override
- DataInterpreter<String> get dataValidator => const StringDataInterpreter();
+ DataInterpreter<Features> get dataValidator =>
+ const FeaturesDataInterpreter();
}