| // Copyright (c) 2017, 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 'package:compiler/src/commandline_options.dart'; | |
| import 'package:compiler/src/common/elements.dart'; | |
| import 'package:compiler/src/compiler.dart'; | |
| import 'package:compiler/src/elements/entities.dart'; | |
| import 'package:compiler/src/elements/names.dart'; | |
| import 'package:compiler/src/js/js.dart' as js; | |
| import 'package:compiler/src/js_model/js_strategy.dart'; | |
| import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld; | |
| import 'package:expect/async_helper.dart'; | |
| import 'package:expect/expect.dart'; | |
| import '../helpers/d8_helper.dart'; | |
| import 'package:compiler/src/util/memory_compiler.dart'; | |
| const String SOURCE = r''' | |
| @pragma('dart2js:noInline') | |
| method1<T>(T t) { | |
| print('method1:'); | |
| print('$t is $T = ${t is T}'); | |
| print('"foo" is $T = ${"foo" is T}'); | |
| print(''); | |
| } | |
| @pragma('dart2js:noInline') | |
| method2<T, S>(S s, T t) { | |
| print('method2:'); | |
| print('$t is $T = ${t is T}'); | |
| print('$s is $T = ${s is T}'); | |
| print('$t is $S = ${t is S}'); | |
| print('$s is $S = ${s is S}'); | |
| print(''); | |
| } | |
| @pragma('dart2js:tryInline') | |
| method3<T, S>(T t, S s) { | |
| print('method3:'); | |
| print('$t is $T = ${t is T}'); | |
| print('$s is $T = ${s is T}'); | |
| print('$t is $S = ${t is S}'); | |
| print('$s is $S = ${s is S}'); | |
| print(''); | |
| } | |
| class Class1<T> { | |
| final int index; | |
| Class1(this.index); | |
| String toString() => 'c$index'; | |
| } | |
| method4<T>(int index) => Class1<T>(index); | |
| testMethod4() { | |
| print('method4:'); | |
| var c1 = method4<int>(1); | |
| var c2 = method4<String>(2); | |
| print('$c1 is Class1<int> = ${c1 is Class1<int>}'); | |
| print('$c2 is Class1<int> = ${c2 is Class1<int>}'); | |
| print('$c1 is Class1<String> = ${c1 is Class1<String>}'); | |
| print('$c2 is Class1<String> = ${c2 is Class1<String>}'); | |
| print(''); | |
| } | |
| class Class2 { | |
| @pragma('dart2js:tryInline') | |
| method5<T>(T t) { | |
| print('Class2.method5:'); | |
| print('$t is $T = ${t is T}'); | |
| print('"foo" is $T = ${"foo" is T}'); | |
| print(''); | |
| } | |
| @pragma('dart2js:noInline') | |
| method6(o) { | |
| print('Class2.method6:'); | |
| print('$o is int = ${o is int}'); | |
| print('$o is String = ${o is String}'); | |
| print(''); | |
| } | |
| } | |
| class Class3 { | |
| @pragma('dart2js:noInline') | |
| method6<T>(T t) { | |
| print('Class3.method6:'); | |
| print('$t is $T = ${t is T}'); | |
| print('"foo" is $T = ${"foo" is T}'); | |
| print(''); | |
| } | |
| } | |
| class Class4<T> { | |
| Function method7<Q>() { | |
| foo(T t, Q q) => ''; | |
| return foo; | |
| } | |
| } | |
| // Nested generic local function. | |
| outside<T>() { | |
| nested<T>(T t) => ''; | |
| return nested; | |
| } | |
| main(args) { | |
| method1<int>(0); | |
| method2<String, double>(0.5, 'foo'); | |
| method3<double, String>(1.5, 'bar'); | |
| testMethod4(); | |
| Class2().method5<int>(0); | |
| Class3().method6<int>(0); | |
| dynamic c3 = args != null ? Class3() : Class2(); | |
| c3.method6(0); // Missing type arguments. | |
| try { | |
| dynamic c2 = args == null ? Class3() : Class2(); | |
| c2.method6(0); // Valid call. | |
| c2.method6<int>(0); // Extra type arguments. | |
| } catch (e) { | |
| print('noSuchMethod: Class2.method6<int>'); | |
| print(''); | |
| } | |
| var c = Class4<bool>(); | |
| print((c.method7<int>()).runtimeType); | |
| outside(); | |
| } | |
| '''; | |
| const String OUTPUT = r''' | |
| method1: | |
| 0 is int = true | |
| "foo" is int = false | |
| method2: | |
| foo is String = true | |
| 0.5 is String = false | |
| foo is double = false | |
| 0.5 is double = true | |
| method3: | |
| 1.5 is double = true | |
| bar is double = false | |
| 1.5 is String = false | |
| bar is String = true | |
| method4: | |
| c1 is Class1<int> = true | |
| c2 is Class1<int> = false | |
| c1 is Class1<String> = false | |
| c2 is Class1<String> = true | |
| Class2.method5: | |
| 0 is int = true | |
| "foo" is int = false | |
| Class3.method6: | |
| 0 is int = true | |
| "foo" is int = false | |
| Class3.method6: | |
| 0 is dynamic = true | |
| "foo" is dynamic = true | |
| Class2.method6: | |
| 0 is int = true | |
| 0 is String = false | |
| noSuchMethod: Class2.method6<int> | |
| (bool, int) => String | |
| '''; | |
| main(List<String> args) { | |
| asyncTest(() async { | |
| D8Result result = await runWithD8( | |
| memorySourceFiles: {'main.dart': SOURCE}, | |
| options: [ | |
| Flags.disableRtiOptimization, | |
| '--libraries-spec=$sdkLibrariesSpecificationUri', | |
| ], | |
| expectedOutput: OUTPUT, | |
| printJs: args.contains('-v'), | |
| ); | |
| Compiler compiler = result.compilationResult.compiler!; | |
| JsBackendStrategy backendStrategy = compiler.backendStrategy; | |
| JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!; | |
| ElementEnvironment elementEnvironment = closedWorld.elementEnvironment; | |
| void checkMethod( | |
| String methodName, { | |
| String? className, | |
| required int expectedParameterCount, | |
| }) { | |
| FunctionEntity? method; | |
| if (className != null) { | |
| ClassEntity? cls = elementEnvironment.lookupClass( | |
| elementEnvironment.mainLibrary!, | |
| className, | |
| ); | |
| Expect.isNotNull(cls, "Class '$className' not found."); | |
| method = elementEnvironment.lookupClassMember( | |
| cls!, | |
| Name(methodName, cls.library.canonicalUri), | |
| ) as FunctionEntity?; | |
| Expect.isNotNull(method, "Method '$methodName' not found in $cls."); | |
| } else { | |
| method = elementEnvironment.lookupLibraryMember( | |
| elementEnvironment.mainLibrary!, | |
| methodName, | |
| ) as FunctionEntity?; | |
| Expect.isNotNull(method, "Method '$methodName' not found."); | |
| } | |
| js.Fun fun = backendStrategy.generatedCode[method] as js.Fun; | |
| Expect.equals( | |
| expectedParameterCount, | |
| fun.params.length, | |
| "Unexpected parameter count for $method:\n${js.nodeToString(fun)}", | |
| ); | |
| } | |
| checkMethod('method1', expectedParameterCount: 2); | |
| checkMethod('method2', expectedParameterCount: 4); | |
| checkMethod('method6', className: 'Class2', expectedParameterCount: 1); | |
| checkMethod('method6', className: 'Class3', expectedParameterCount: 2); | |
| }); | |
| } |