2bb32ec55a
Change-Id: Ib1afd38a7d3694c2e5ed591132f41b147f76d75a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427560 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Mayank Patke <fishythefish@google.com>
431 lines
14 KiB
Dart
431 lines
14 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.
|
|
|
|
import 'dart:io';
|
|
import 'package:compiler/src/common/elements.dart';
|
|
import 'package:compiler/src/elements/entities.dart';
|
|
import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld;
|
|
import 'package:expect/async_helper.dart';
|
|
import 'package:expect/expect.dart';
|
|
import 'package:compiler/src/util/memory_compiler.dart';
|
|
|
|
enum Kind { regular, native, jsInterop }
|
|
|
|
main() {
|
|
asyncTest(() async {
|
|
await runTest(
|
|
'tests/web/jsinterop_test.dart',
|
|
'',
|
|
{
|
|
'Class': Kind.regular,
|
|
'JsInteropClass': Kind.jsInterop,
|
|
'flag': Kind.regular,
|
|
'topLevelField': Kind.regular,
|
|
'topLevelGetter': Kind.regular,
|
|
'topLevelSetter': Kind.regular,
|
|
'topLevelFunction': Kind.regular,
|
|
'externalTopLevelGetter': Kind.jsInterop,
|
|
'externalTopLevelSetter': Kind.jsInterop,
|
|
'externalTopLevelFunction': Kind.jsInterop,
|
|
'externalTopLevelJsInteropGetter': Kind.jsInterop,
|
|
'externalTopLevelJsInteropSetter': Kind.jsInterop,
|
|
'externalTopLevelJsInteropFunction': Kind.jsInterop,
|
|
'Class.generative': Kind.regular,
|
|
'Class.fact': Kind.regular,
|
|
'Class.instanceField': Kind.regular,
|
|
'Class.instanceGetter': Kind.regular,
|
|
'Class.instanceSetter': Kind.regular,
|
|
'Class.instanceMethod': Kind.regular,
|
|
'Class.staticField': Kind.regular,
|
|
'Class.staticGetter': Kind.regular,
|
|
'Class.staticSetter': Kind.regular,
|
|
'Class.staticMethod': Kind.regular,
|
|
'JsInteropClass.externalGenerative': Kind.jsInterop,
|
|
'JsInteropClass.externalFact': Kind.jsInterop,
|
|
'JsInteropClass.externalJsInteropGenerative': Kind.jsInterop,
|
|
'JsInteropClass.externalJsInteropFact': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceMethod': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticMethod': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceJsInteropGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceJsInteropSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceJsInteropMethod': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticJsInteropGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticJsInteropSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticJsInteropMethod': Kind.jsInterop,
|
|
},
|
|
skipList: [
|
|
// TODO(34174): Js-interop fields should not be allowed.
|
|
'01',
|
|
'02',
|
|
'03',
|
|
'04',
|
|
'38',
|
|
'42',
|
|
'46',
|
|
'51',
|
|
// TODO(33834): Non-external constructors should not be allowed.
|
|
'35',
|
|
'37',
|
|
// TODO(34345): Non-external static members should not be allowed.
|
|
'43',
|
|
'44',
|
|
'45',
|
|
'52',
|
|
'53',
|
|
'54',
|
|
],
|
|
);
|
|
await runTest(
|
|
'tests/web/non_jsinterop_test.dart',
|
|
'',
|
|
{
|
|
'Class': Kind.regular,
|
|
'JsInteropClass': Kind.jsInterop,
|
|
'flag': Kind.regular,
|
|
'topLevelField': Kind.regular,
|
|
'topLevelGetter': Kind.regular,
|
|
'topLevelSetter': Kind.regular,
|
|
'topLevelFunction': Kind.regular,
|
|
'externalTopLevelJsInteropGetter': Kind.jsInterop,
|
|
'externalTopLevelJsInteropSetter': Kind.jsInterop,
|
|
'externalTopLevelJsInteropFunction': Kind.jsInterop,
|
|
'Class.generative': Kind.regular,
|
|
'Class.fact': Kind.regular,
|
|
'Class.instanceField': Kind.regular,
|
|
'Class.instanceGetter': Kind.regular,
|
|
'Class.instanceSetter': Kind.regular,
|
|
'Class.instanceMethod': Kind.regular,
|
|
'Class.staticField': Kind.regular,
|
|
'Class.staticGetter': Kind.regular,
|
|
'Class.staticSetter': Kind.regular,
|
|
'Class.staticMethod': Kind.regular,
|
|
'JsInteropClass.externalGenerative': Kind.jsInterop,
|
|
'JsInteropClass.externalFact': Kind.jsInterop,
|
|
'JsInteropClass.externalJsInteropGenerative': Kind.jsInterop,
|
|
'JsInteropClass.externalJsInteropFact': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceMethod': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticMethod': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceJsInteropGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceJsInteropSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalInstanceJsInteropMethod': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticJsInteropGetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticJsInteropSetter': Kind.jsInterop,
|
|
'JsInteropClass.externalStaticJsInteropMethod': Kind.jsInterop,
|
|
},
|
|
skipList: [
|
|
// TODO(34174): Js-interop fields should not be allowed.
|
|
'01',
|
|
'02',
|
|
'03',
|
|
'04',
|
|
'38',
|
|
'42',
|
|
'46',
|
|
'51',
|
|
// TODO(33834): Non-external constructors should not be allowed.
|
|
'35',
|
|
'37',
|
|
// TODO(34345): Non-external static members should not be allowed.
|
|
'43',
|
|
'44',
|
|
'45',
|
|
'52',
|
|
'53',
|
|
'54',
|
|
],
|
|
);
|
|
|
|
await runTest(
|
|
'tests/web/native/native_test.dart',
|
|
'tests/web/native/',
|
|
{
|
|
'Class': Kind.regular,
|
|
'NativeClass': Kind.native,
|
|
'topLevelField': Kind.regular,
|
|
'topLevelGetter': Kind.regular,
|
|
'topLevelSetter': Kind.regular,
|
|
'topLevelFunction': Kind.regular,
|
|
'nativeTopLevelGetter': Kind.native,
|
|
'nativeTopLevelSetter': Kind.native,
|
|
'nativeTopLevelFunction': Kind.native,
|
|
'Class.generative': Kind.regular,
|
|
'Class.fact': Kind.regular,
|
|
'Class.instanceField': Kind.regular,
|
|
'Class.instanceGetter': Kind.regular,
|
|
'Class.instanceSetter': Kind.regular,
|
|
'Class.instanceMethod': Kind.regular,
|
|
'Class.staticField': Kind.regular,
|
|
'Class.staticGetter': Kind.regular,
|
|
'Class.staticSetter': Kind.regular,
|
|
'Class.staticMethod': Kind.regular,
|
|
'Class.nativeInstanceGetter': Kind.native,
|
|
'Class.nativeInstanceSetter': Kind.native,
|
|
'Class.nativeInstanceMethod': Kind.native,
|
|
'NativeClass.generative': Kind.regular,
|
|
'NativeClass.fact': Kind.regular,
|
|
'NativeClass.nativeGenerative': Kind.native,
|
|
'NativeClass.nativeFact': Kind.native,
|
|
'NativeClass.instanceField': Kind.native,
|
|
'NativeClass.instanceNamedField': Kind.native,
|
|
'NativeClass.instanceGetter': Kind.regular,
|
|
'NativeClass.instanceSetter': Kind.regular,
|
|
'NativeClass.instanceMethod': Kind.regular,
|
|
'NativeClass.staticField': Kind.regular,
|
|
'NativeClass.staticGetter': Kind.regular,
|
|
'NativeClass.staticSetter': Kind.regular,
|
|
'NativeClass.staticMethod': Kind.regular,
|
|
'NativeClass.nativeInstanceGetter': Kind.native,
|
|
'NativeClass.nativeInstanceSetter': Kind.native,
|
|
'NativeClass.nativeInstanceMethod': Kind.native,
|
|
'NativeClass.nativeStaticGetter': Kind.native,
|
|
'NativeClass.nativeStaticSetter': Kind.native,
|
|
'NativeClass.nativeStaticMethod': Kind.native,
|
|
},
|
|
skipList: [
|
|
// External constructors in non-native class
|
|
//'08',
|
|
//'09',
|
|
// External instance members in non-native class
|
|
//'22',
|
|
//'23',
|
|
//'24',
|
|
// External static members in non-native class
|
|
//'25',
|
|
//'26',
|
|
//'27',
|
|
// External instance members in native class
|
|
//'36',
|
|
//'37',
|
|
//'38',
|
|
// External static members in native class
|
|
//'39',
|
|
//'40',
|
|
//'41',
|
|
],
|
|
);
|
|
});
|
|
}
|
|
|
|
runTest(
|
|
String fileName,
|
|
String location,
|
|
Map<String, Kind> expectations, {
|
|
List<String> skipList = const <String>[],
|
|
}) async {
|
|
print('--------------------------------------------------------------------');
|
|
print('Testing $fileName');
|
|
print('--------------------------------------------------------------------');
|
|
String test = File(fileName).readAsStringSync();
|
|
|
|
List<String> commonLines = <String>[];
|
|
Map<String, SubTest> subTests = <String, SubTest>{};
|
|
|
|
int lineIndex = 0;
|
|
for (String line in test.split('\n')) {
|
|
int index = line.indexOf('//#');
|
|
if (index != -1) {
|
|
String prefix = line.substring(0, index);
|
|
String suffix = line.substring(index + 3);
|
|
String name = suffix.substring(0, suffix.indexOf((':'))).trim();
|
|
SubTest subTest = subTests.putIfAbsent(name, () => SubTest());
|
|
subTest.lines[lineIndex] = line;
|
|
int commentIndex = prefix.indexOf('// ');
|
|
if (commentIndex != -1) {
|
|
String combinedErrors = prefix.substring(commentIndex + 3);
|
|
for (String error in combinedErrors.split(',')) {
|
|
subTest.expectedErrors.add(error.trim());
|
|
}
|
|
}
|
|
commonLines.add('');
|
|
} else {
|
|
commonLines.add(line);
|
|
}
|
|
lineIndex++;
|
|
}
|
|
|
|
String path = '${location}main.dart';
|
|
Uri entryPoint = Uri.parse('memory:$path');
|
|
await runPositiveTest(entryPoint, {
|
|
path: commonLines.join('\n'),
|
|
}, expectations);
|
|
for (String name in subTests.keys) {
|
|
if (!skipList.contains(name)) {
|
|
SubTest subTest = subTests[name]!;
|
|
await runNegativeTest(subTest, entryPoint, {
|
|
path: subTest.generateCode(commonLines),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
runPositiveTest(
|
|
Uri entryPoint,
|
|
Map<String, String> sources,
|
|
Map<String, Kind> expectations,
|
|
) async {
|
|
CompilationResult result = await runCompiler(
|
|
entryPoint: entryPoint,
|
|
memorySourceFiles: sources,
|
|
);
|
|
Expect.isTrue(result.isSuccess);
|
|
|
|
JClosedWorld closedWorld = result.compiler!.backendClosedWorldForTesting!;
|
|
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
|
|
|
|
void checkClass(
|
|
ClassEntity cls, {
|
|
bool isNative = false,
|
|
bool isJsInterop = false,
|
|
}) {
|
|
if (isJsInterop) {
|
|
isNative = true;
|
|
}
|
|
Expect.equals(
|
|
isJsInterop,
|
|
closedWorld.nativeData.isJsInteropClass(cls),
|
|
"Unexpected js interop class result for $cls.",
|
|
);
|
|
Expect.equals(
|
|
isNative,
|
|
closedWorld.nativeData.isNativeClass(cls),
|
|
"Unexpected native class result for $cls.",
|
|
);
|
|
if (isJsInterop) {
|
|
Expect.isTrue(
|
|
closedWorld.nativeData.isJsInteropLibrary(cls.library),
|
|
"Unexpected js interop library result for ${cls.library}.",
|
|
);
|
|
}
|
|
}
|
|
|
|
void checkMember(
|
|
MemberEntity member, {
|
|
bool isNative = false,
|
|
bool isJsInterop = false,
|
|
}) {
|
|
if (isJsInterop) {
|
|
isNative = true;
|
|
}
|
|
Expect.equals(
|
|
isJsInterop,
|
|
closedWorld.nativeData.isJsInteropMember(member),
|
|
"Unexpected js interop member result for $member.",
|
|
);
|
|
Expect.equals(
|
|
isNative,
|
|
closedWorld.nativeData.isNativeMember(member),
|
|
"Unexpected native member result for $member.",
|
|
);
|
|
if (isJsInterop) {
|
|
Expect.isTrue(
|
|
closedWorld.nativeData.isJsInteropLibrary(member.library),
|
|
"Unexpected js interop library result for ${member.library}.",
|
|
);
|
|
}
|
|
}
|
|
|
|
elementEnvironment.forEachLibraryMember(elementEnvironment.mainLibrary!, (
|
|
MemberEntity member,
|
|
) {
|
|
if (member == elementEnvironment.mainFunction) return;
|
|
|
|
Kind? kind = expectations.remove(member.name);
|
|
Expect.isNotNull(kind, "No expectations for $member");
|
|
checkMember(
|
|
member,
|
|
isNative: kind == Kind.native,
|
|
isJsInterop: kind == Kind.jsInterop,
|
|
);
|
|
});
|
|
|
|
elementEnvironment.forEachClass(elementEnvironment.mainLibrary!, (
|
|
ClassEntity cls,
|
|
) {
|
|
Kind? kind = expectations.remove(cls.name);
|
|
Expect.isNotNull(kind, "No expectations for $cls");
|
|
checkClass(
|
|
cls,
|
|
isNative: kind == Kind.native,
|
|
isJsInterop: kind == Kind.jsInterop,
|
|
);
|
|
|
|
checkClassMember(MemberEntity member) {
|
|
Kind? kind = expectations.remove('${cls.name}.${member.name}');
|
|
Expect.isNotNull(kind, "No expectations for $member");
|
|
checkMember(
|
|
member,
|
|
isNative: kind == Kind.native,
|
|
isJsInterop: kind == Kind.jsInterop,
|
|
);
|
|
}
|
|
|
|
elementEnvironment.forEachConstructor(cls, checkClassMember);
|
|
elementEnvironment.forEachLocalClassMember(cls, checkClassMember);
|
|
});
|
|
|
|
Expect.isTrue(expectations.isEmpty, "Untested expectations: $expectations");
|
|
}
|
|
|
|
runNegativeTest(
|
|
SubTest subTest,
|
|
Uri entryPoint,
|
|
Map<String, String> sources,
|
|
) async {
|
|
DiagnosticCollector collector = DiagnosticCollector();
|
|
CompilationResult result = await runCompiler(
|
|
entryPoint: entryPoint,
|
|
memorySourceFiles: sources,
|
|
diagnosticHandler: collector,
|
|
);
|
|
Expect.isFalse(
|
|
result.isSuccess,
|
|
"Expected compile time error(s) for\n$subTest",
|
|
);
|
|
List<String> expected = subTest.expectedErrors
|
|
.map((error) => 'MessageKind.' + error)
|
|
.toList();
|
|
List<String> actual = collector.errors
|
|
.map((error) => error.messageKind.toString())
|
|
.toList();
|
|
expected.sort();
|
|
actual.sort();
|
|
Expect.listEquals(
|
|
expected,
|
|
actual,
|
|
"Unexpected compile time error(s) for\n$subTest",
|
|
);
|
|
}
|
|
|
|
class SubTest {
|
|
List<String> expectedErrors = [];
|
|
final Map<int, String> lines = <int, String>{};
|
|
|
|
String generateCode(List<String> commonLines) {
|
|
StringBuffer sb = StringBuffer();
|
|
int i = 0;
|
|
while (i < commonLines.length) {
|
|
if (lines.containsKey(i)) {
|
|
sb.writeln(lines[i]);
|
|
} else {
|
|
sb.writeln(commonLines[i]);
|
|
}
|
|
i++;
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return lines.values.join('\n');
|
|
}
|
|
}
|