| // 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. |
| |
| // @dart = 2.7 |
| |
| import 'dart:io'; |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:compiler/src/common_elements.dart'; |
| import 'package:compiler/src/elements/entities.dart'; |
| import 'package:compiler/src/world.dart'; |
| import 'package:expect/expect.dart'; |
| import '../helpers/memory_compiler.dart'; |
| |
| enum Kind { |
| regular, |
| native, |
| jsInterop, |
| } |
| |
| main() { |
| asyncTest(() async { |
| await runTest('tests/web_2/jsinterop_test.dart', '', { |
| 'Class': Kind.regular, |
| 'JsInteropClass': Kind.jsInterop, |
| '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_2/non_jsinterop_test.dart', '', { |
| 'Class': Kind.regular, |
| 'JsInteropClass': Kind.jsInterop, |
| '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_2/native/native_test.dart', 'tests/web_2/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.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 = new 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, () => new 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 = new 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 = new 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'); |
| } |
| } |