// 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) { print('method1:'); print('$t is $T = ${t is T}'); print('"foo" is $T = ${"foo" is T}'); print(''); } @pragma('dart2js:noInline') method2(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 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 { final int index; Class1(this.index); String toString() => 'c$index'; } method4(int index) => Class1(index); testMethod4() { print('method4:'); var c1 = method4(1); var c2 = method4(2); print('$c1 is Class1 = ${c1 is Class1}'); print('$c2 is Class1 = ${c2 is Class1}'); print('$c1 is Class1 = ${c1 is Class1}'); print('$c2 is Class1 = ${c2 is Class1}'); print(''); } class Class2 { @pragma('dart2js:tryInline') method5(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) { print('Class3.method6:'); print('$t is $T = ${t is T}'); print('"foo" is $T = ${"foo" is T}'); print(''); } } class Class4 { Function method7() { foo(T t, Q q) => ''; return foo; } } // Nested generic local function. outside() { nested(T t) => ''; return nested; } main(args) { method1(0); method2(0.5, 'foo'); method3(1.5, 'bar'); testMethod4(); Class2().method5(0); Class3().method6(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(0); // Extra type arguments. } catch (e) { print('noSuchMethod: Class2.method6'); print(''); } var c = Class4(); print((c.method7()).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 = true c2 is Class1 = false c1 is Class1 = false c2 is Class1 = 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 (bool, int) => String '''; main(List 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); }); }