| // 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: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/compiler/dart2js_extra/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. | |
| '34', | |
| '35', | |
| '36', | |
| '37', | |
| // TODO(34345): Non-external static members should not be allowed. | |
| '43', | |
| '44', | |
| '45', | |
| '52', | |
| '53', | |
| '54', | |
| ]); | |
| await runTest('tests/compiler/dart2js_extra/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. | |
| '34', | |
| '35', | |
| '36', | |
| '37', | |
| // TODO(34345): Non-external static members should not be allowed. | |
| '43', | |
| '44', | |
| '45', | |
| '52', | |
| '53', | |
| '54', | |
| ]); | |
| // TODO(johnniwinther): Add similar test for native declarations. | |
| }); | |
| } | |
| 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) { | |
| subTest.expectedError = prefix.substring(commentIndex + 3).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 for\n$subTest"); | |
| Expect.equals( | |
| 1, collector.errors.length, "Expected compile time error for\n$subTest"); | |
| Expect.equals( | |
| 'MessageKind.${subTest.expectedError}', | |
| collector.errors.first.messageKind.toString(), | |
| "Unexpected compile time error for\n$subTest"); | |
| } | |
| class SubTest { | |
| String expectedError; | |
| 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(); | |
| } | |
| String toString() { | |
| return lines.values.join('\n'); | |
| } | |
| } |